to_excel 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,87 @@
1
+ = Introduction
2
+ I first used the good "arydjmal/to_xls" plugin but it doesn't allow collections of multiples objects from different classes and can't export
3
+ associations attributes of a class.
4
+
5
+ With to_excel gem you can export multiples collections of objects of different classes, moreover you have the possibility of
6
+ exporting associations's attributes (recursively).
7
+
8
+ = Usage
9
+
10
+ == Basic usage
11
+ @users = User.all
12
+
13
+ ==== With one collection. All columns are exported with default humanized attributes names for excel columns :
14
+
15
+ [@users].to_excel
16
+
17
+ ==== If you don't want excel header :
18
+
19
+ @users.to_excel headers => false
20
+
21
+ ==== Personalized header
22
+
23
+ header = ['User Id', 'User name']
24
+ [@users].to_excel(:map => { User => [:id,:name], :headers => header)
25
+
26
+
27
+ ==== To restrict attributes :
28
+
29
+ [@users].to_excel(:map => {User => [:age, :id, :name]}) #User is the class of users objects.
30
+
31
+
32
+ == Multiples collections of different classes
33
+ Suppose you have two classes : User and Computer, with Computer has_one :user
34
+
35
+ @users = [User.new(:name => 'Dupont', :age => 25)]
36
+ @computers =[Computer.new(:brand => 'Apple', :portable => true, :user => @users.first)]
37
+
38
+ ==== You can export in the same file users and computers (in this order)
39
+
40
+ [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id, :brand,]})
41
+
42
+ ==== You can export nested attributes, for example name of user of computer
43
+ In order to export the name column of the user of the computer, include [:user,:name] in the attribute list of key Computer :
44
+
45
+ [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id, :brand, [:user, :name]]})
46
+
47
+
48
+ ==== You can export value of a instance method
49
+
50
+ Suppose Computer class has an instance method macintosh?
51
+ You can export the column values as if it was an attribute :
52
+
53
+ [@computers].to_excel(:map => {Computer => [:id, :macintosh?]})
54
+
55
+
56
+ ==== Controller side
57
+
58
+ class UsersController < ....
59
+
60
+ ....
61
+ send_data [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id, :brand,]})
62
+
63
+ ....
64
+ end
65
+
66
+ If you want to use it with respond_to, you may have to include in initializes/mime_types :
67
+
68
+ Mime::Type.register "application/vnd.ms-excel", :xls
69
+
70
+
71
+ == Dependencies
72
+
73
+ No dependency.
74
+
75
+ == Install
76
+
77
+ sudo gem install to_excel
78
+
79
+
80
+
81
+ Copyright © 2010 Philippe Cantin, released under the MIT license.
82
+
83
+
84
+
85
+
86
+
87
+
@@ -0,0 +1,40 @@
1
+ class Array
2
+ def to_excel(options = {})
3
+ output = '<?xml version="1.0" encoding="UTF-8"?><Workbook xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40" xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office"><Worksheet ss:Name="Sheet1"><Table>'
4
+
5
+ if self.first.is_a?(Array) && self.first.any?
6
+ unless options[:headers] == false
7
+ output << "<Row>"
8
+
9
+ if options[:headers].is_a? Array
10
+ options[:headers].each do |title|
11
+ output << "<Cell><Data ss:Type=\"String\">#{title}</Data></Cell>"
12
+ end
13
+ else
14
+ klasses = self.map(&:first).map(&:class)
15
+ klasses.each do |klass|
16
+ options[:map][klass].each do |attribute|
17
+ output << "<Cell><Data ss:Type=\"String\">#{klass.human_attribute_name(attribute)}</Data></Cell>"
18
+ end
19
+ end
20
+ end
21
+
22
+ output << "</Row>"
23
+ end
24
+
25
+ (0..self.map(&:length).max - 1).each do |n|
26
+ items = self.map {|collection| collection[n] || collection.first.class.new}
27
+ output << "<Row>"
28
+ items.each do |item|
29
+ options[:map][item.class].each do |column|
30
+ value = column.is_a?(Array) ? column.inject(item){|result,column| result.send(column) if result} : item.send(column)
31
+ output << "<Cell><Data ss:Type=\"#{value.is_a?(Integer) ? 'Number' : 'String'}\">#{value}</Data></Cell>"
32
+ end
33
+ end
34
+ output << "</Row>"
35
+ end
36
+ end
37
+
38
+ output << '</Table></Worksheet></Workbook>'
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'active_support'
5
+ require 'to_excel'
6
+ end
7
+
8
+ class User
9
+ COLUMNS = %w(id name age)
10
+
11
+ attr_accessor *COLUMNS
12
+
13
+ def self.human_attribute_name(attribute)
14
+ attribute.to_s.humanize
15
+ end
16
+
17
+ def initialize(params={})
18
+ params.each { |key, value| self.send("#{key}=", value); }
19
+ self
20
+ end
21
+
22
+ def attributes
23
+ COLUMNS.inject({}) { |attributes, attribute| attributes.merge(attribute => send(attribute)) }
24
+ end
25
+
26
+ def is_old?
27
+ age > 40
28
+ end
29
+ end
30
+
31
+ class Computer
32
+ COLUMNS = %w(id brand portable user)
33
+
34
+ attr_accessor *COLUMNS
35
+
36
+ def self.human_attribute_name(attribute)
37
+ attribute.to_s.humanize
38
+ end
39
+
40
+ def initialize(params={})
41
+ params.each { |key, value| self.send("#{key}=", value); }
42
+ self
43
+ end
44
+
45
+ def attributes
46
+ COLUMNS.inject({}) { |attributes, attribute| attributes.merge(attribute => send(attribute)) }
47
+ end
48
+
49
+ end
@@ -0,0 +1,104 @@
1
+ require 'to_excel_helper'
2
+
3
+ class ToExcelTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @users = [
7
+ User.new(:id => 1, :name => 'Dupont', :age => 25),
8
+ User.new(:id => 2, :name => 'Martin', :age => 22)
9
+ ]
10
+ @computers =[
11
+ Computer.new(:id => 1, :brand => 'Apple', :portable => true, :user => @users.first),
12
+ Computer.new(:id => 2, :brand => 'Dell', :portable => false, :user => @users[1])
13
+ ]
14
+ end
15
+
16
+ def test_should_be_array_of_arrays_not_empty
17
+ assert_equal build_document(nil), [].to_excel
18
+ assert_equal build_document(nil), [[]].to_excel
19
+ assert_equal build_document(nil), @users.to_excel
20
+ end
21
+
22
+ def test_with_no_headers
23
+ document = build_document(
24
+ row(cell('Number', '25'), cell('Number', '1'), cell('String', 'Dupont')),
25
+ row(cell('Number', '22'), cell('Number', '2'), cell('String', 'Martin'))
26
+ )
27
+ assert_equal document, [@users].to_excel(:headers => false, :map => {User => [:age, :id, :name]})
28
+ end
29
+
30
+ def test_with_methods
31
+ document = build_document(
32
+ row(cell('String', 'Age'), cell('String', 'Id'), cell('String', 'Name'), cell('String', 'Is old?')),
33
+ row(cell('Number', '25'), cell('Number', '1'), cell('String', 'Dupont'), cell('String', 'false')),
34
+ row(cell('Number', '22'), cell('Number', '2'), cell('String', 'Martin'), cell('String', 'false'))
35
+ )
36
+ assert_equal document, [@users].to_excel(:map => { User => [:age, :id, :name, :is_old?]})
37
+ end
38
+
39
+ def test_with_nested_attribute
40
+ document = build_document(
41
+ row(cell('String', 'Id'), cell('String', 'Username')),
42
+ row(cell('Number', '1'), cell('String', 'Dupont')),
43
+ row(cell('Number', '2'), cell('String', 'Martin'))
44
+ )
45
+ assert_equal document, [@computers].to_excel(:map => { Computer => [:id, [:user, :name]]})
46
+ end
47
+
48
+ def test_with_two_collections
49
+ document = build_document(
50
+ row(cell('String', 'Id'), cell('String', 'Name'), cell('String', 'Id'), cell('String', 'Brand')),
51
+ row(cell('Number', '1'), cell('String', 'Dupont'), cell('Number', '1'), cell('String', 'Apple')),
52
+ row(cell('Number', '2'), cell('String', 'Martin'), cell('Number', '2'), cell('String', 'Dell'))
53
+ )
54
+ assert_equal document, [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id,:brand]})
55
+ end
56
+
57
+ def test_with_one_collection
58
+ document = build_document(
59
+ row(cell('String', 'Id'), cell('String', 'Username')),
60
+ row(cell('Number', '1'), cell('String', 'Dupont')),
61
+ row(cell('Number', '2'), cell('String', 'Martin'))
62
+ )
63
+ assert_equal document, [@computers].to_excel(:map => {Computer => [:id,[:user, :name]]})
64
+ end
65
+
66
+ def test_should_render_empty_cells_unless_collections_of_same_size
67
+ @users << User.new(:id => 3, :name => 'Durand', :age => 23)
68
+ document = build_document(
69
+ row(cell('String', 'Id'), cell('String', 'Name'), cell('String', 'Id'), cell('String', 'Brand')),
70
+ row(cell('Number', '1'), cell('String', 'Dupont'), cell('Number', '1'), cell('String', 'Apple')),
71
+ row(cell('Number', '2'), cell('String', 'Martin'), cell('Number', '2'), cell('String', 'Dell')),
72
+ row(cell('Number', '3'), cell('String', 'Durand'), cell('String', ''), cell('String', ''))
73
+ )
74
+ assert_equal document, [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id,:brand]})
75
+ end
76
+
77
+ def test_with_own_header
78
+ document = build_document(
79
+ row(cell('String', 'User Id'), cell('String', 'Nom'), cell('String', 'Computer Id'), cell('String', 'Marque')),
80
+ row(cell('Number', '1'), cell('String', 'Dupont'), cell('Number', '1'), cell('String', 'Apple')),
81
+ row(cell('Number', '2'), cell('String', 'Martin'), cell('Number', '2'), cell('String', 'Dell'))
82
+ )
83
+ header = ['User Id', 'Nom', 'Computer Id', 'Marque']
84
+ assert_equal document, [@users, @computers].to_excel(:map => { User => [:id,:name], Computer => [:id,:brand]}, :headers => header)
85
+ end
86
+ #TODO generate default map from class columns if no map option passed
87
+ def test_with_no_map_option
88
+
89
+ end
90
+
91
+ private
92
+
93
+ def build_document(*rows)
94
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Workbook xmlns:x=\"urn:schemas-microsoft-com:office:excel\" xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" xmlns:html=\"http://www.w3.org/TR/REC-html40\" xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" xmlns:o=\"urn:schemas-microsoft-com:office:office\"><Worksheet ss:Name=\"Sheet1\"><Table>#{rows}</Table></Worksheet></Workbook>"
95
+ end
96
+
97
+ def row(*cells)
98
+ "<Row>#{cells}</Row>"
99
+ end
100
+
101
+ def cell(type, content)
102
+ "<Cell><Data ss:Type=\"#{type}\">#{content}</Data></Cell>"
103
+ end
104
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: to_excel
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ version: "1.0"
9
+ platform: ruby
10
+ authors:
11
+ - Philippe Cantin
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-05-01 00:00:00 +02:00
17
+ default_executable:
18
+ dependencies: []
19
+
20
+ description: Export ruby objects to excel file
21
+ email:
22
+ executables: []
23
+
24
+ extensions: []
25
+
26
+ extra_rdoc_files:
27
+ - README.rdoc
28
+ files:
29
+ - lib/to_excel.rb
30
+ - README.rdoc
31
+ has_rdoc: true
32
+ homepage: http://github.com/anoiaque/to_excel
33
+ licenses: []
34
+
35
+ post_install_message:
36
+ rdoc_options: []
37
+
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.6
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Export ruby objects to excel file. Allow many collections of different classpec. Can include associations attributespec.
61
+ test_files:
62
+ - test/to_excel_helper.rb
63
+ - test/to_excel_test.rb