table_setting 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 63c40c25789795050dc7263feef891495355d4fa
4
+ data.tar.gz: c65c25bba4e6335aa6706387764a153553462472
5
+ SHA512:
6
+ metadata.gz: 5d5bc712d9a5e61aa1a6dcd3a4cba1255c89a12b46c604cadc0dd31f85e3813a402d9e25005a989a4450667e07ddfeac86ed24812d2e02a4147f9f919e59f168
7
+ data.tar.gz: adc6634d47602631a50b561c4d0358fe58520e3b22bfdc388bcaf62e985483b2f0bf50bff9d81622fdcee60af834b527eebe64d5e66c211070c3d2cbf1b10fff
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in table_setting.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,17 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ table_setting (0.2.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (10.1.0)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ bundler (~> 1.3)
16
+ rake
17
+ table_setting!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Todd Gehman
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,51 @@
1
+ # TableSetting
2
+
3
+ I couldn't find a gem that would allow an HTML preview of a styled Excel spreadsheet before exporting, so I had to roll my own. Styles can be applied to individual cells, entire rows, or (once the table is built) even columns. To save some bandwidth, styles are applied using automatically-defined classes which are then applied to the corresponding cells.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'table_setting'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install table_setting
18
+
19
+ ## Usage
20
+
21
+ The following simple example creates a table which has a header featuring large white text on a dark brown background. The bottom right cell is individually set to bold text and the bottom row spans all columns. Exporting with to_html or to_xls achieve roughly the same formatting.
22
+
23
+ sheet = TableSetting::Sheet.new
24
+ header = sheet.new_row(background: '#3A2212', color: '#ffffff', bold: true, size: '18px')
25
+ header.add_cells(["First Name", "Last Name"])
26
+
27
+ sheet.new_row.add_cells(["Fred", "Flintstone"])
28
+ row = sheet.new_row
29
+ row.new_cell("Wilma")
30
+ row.new_cell("Flintstone", bold: true)
31
+
32
+ footer = sheet.new_row
33
+ footer.new_cell("That is all the people to list.", span: 'all')
34
+
35
+ Then export:
36
+
37
+ sheet.to_html
38
+ sheet.to_xls
39
+ sheet.to_csv
40
+
41
+ Or maybe you want to turn the whole second column red:
42
+
43
+ sheet.style_column(2, background: '#ff6666')
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,131 @@
1
+ class TableSetting::Cell
2
+ attr_reader :row, :contents, :span, :style, :left_index
3
+
4
+ def initialize(row, contents, options = {})
5
+ @row = row
6
+ @row.cells.push(self)
7
+
8
+ @contents = contents
9
+ @span = options[:span] || 1
10
+
11
+ @style = TableSetting::Style.new(self, options)
12
+
13
+ @left_index = column_index
14
+ end
15
+
16
+ def set_style(options)
17
+ style.update(options)
18
+ end
19
+
20
+
21
+ def sheet
22
+ row.sheet
23
+ end
24
+
25
+ def bold?
26
+ @bold || row.bold
27
+ end
28
+
29
+ def background
30
+ @background || row.background
31
+ end
32
+
33
+ def color
34
+ @color || row.color
35
+ end
36
+
37
+ def size
38
+ @size || row.size
39
+ end
40
+
41
+ def in_column?(number)
42
+ left_index == (number - 1)
43
+ end
44
+
45
+ def preceding_cells
46
+ list = []
47
+ my_index = row.cells.index(self)
48
+ row.cells.each do |cell|
49
+ if row.cells.index(cell) < my_index
50
+ list << cell
51
+ else
52
+ break
53
+ end
54
+ end
55
+ list
56
+ end
57
+
58
+ def to_html
59
+ <<-HTML
60
+ <td #{html_classes} #{span_attribute}>#{contents.blank? ? '&nbsp;' : contents}</td>
61
+ HTML
62
+ end
63
+
64
+ def style_name
65
+ style.name
66
+ end
67
+
68
+ def style_css
69
+ style.to_css
70
+ end
71
+
72
+ def style_xls
73
+ style.to_xls_xml
74
+ end
75
+
76
+ def to_xls
77
+ colspan = 1
78
+ if span == 'all'
79
+ colspan = sheet.num_columns
80
+ elsif span
81
+ colspan = span
82
+ end
83
+
84
+ if colspan > 1
85
+ column_attribute = %Q{ss:MergeAcross="#{colspan - 1}"}
86
+ end
87
+
88
+ %Q{<Cell #{column_attribute} ss:StyleID="#{style.name}"><Data ss:Type="String">#{contents}</Data></Cell>\n}
89
+ end
90
+
91
+ # def left_index
92
+ # preceeding_cells.map{|c| c.span}.sum
93
+ # end
94
+
95
+ private
96
+
97
+ def html_classes
98
+ list = []
99
+ if bold?
100
+ list << 'bold'
101
+ end
102
+ list.push(style.name)
103
+ unless list.empty?
104
+ return %Q{class="#{list.join(' ')}"}
105
+ end
106
+ ''
107
+ end
108
+
109
+ def span_attribute
110
+ span = 0
111
+ if @span == 'all'
112
+ span = sheet.num_columns
113
+ elsif @span
114
+ span = @span
115
+ end
116
+ if span > 0
117
+ return %Q{colspan="#{span}"}
118
+ end
119
+ ''
120
+ end
121
+
122
+ def column_index
123
+ lefties = preceding_cells
124
+ if lefties.empty?
125
+ return 0
126
+ end
127
+ lefties.map{|c| c.span}.sum
128
+ end
129
+
130
+
131
+ end
@@ -0,0 +1,14 @@
1
+ class TableSetting::Column
2
+ def initialize(title, method, options = {})
3
+ @title = title
4
+ @method = method
5
+ end
6
+
7
+ def title
8
+ @title
9
+ end
10
+
11
+ def method
12
+ @method
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ class TableSetting::Row
2
+ attr_reader :sheet, :bold, :background, :color, :size
3
+ attr_accessor :cells
4
+ def initialize(sheet, options = {})
5
+ @sheet = sheet
6
+ @cells = []
7
+ @sheet.rows.push(self)
8
+ @bold = options[:bold] || nil
9
+ @background = options[:background] || nil
10
+ @color = options[:color] || nil
11
+ @size = options[:size] || nil
12
+ end
13
+
14
+ def num_columns
15
+ total_width = 0
16
+ cells.each do |cell|
17
+ width = 1
18
+ if cell.span and
19
+ cell.span != 'all' and
20
+ cell.span > 1
21
+ width = cell.span
22
+ end
23
+ total_width += width
24
+ end
25
+ total_width
26
+ end
27
+
28
+ def bold?
29
+ bold
30
+ end
31
+
32
+ def new_cell(contents, options = {})
33
+ TableSetting::Cell.new(self, contents, options)
34
+ end
35
+
36
+ def add_cells(list)
37
+ list.map{|contents| self.new_cell(contents)}
38
+ end
39
+
40
+ def to_a
41
+ cells.map(&:contents)
42
+ end
43
+
44
+ def to_html
45
+ <<-HTML
46
+ <tr>
47
+ #{cells.map(&:to_html).join("\n")}
48
+ </tr>
49
+ HTML
50
+ end
51
+
52
+ def filled?
53
+ cells.each do |cell|
54
+ return true if cell.span == 'all'
55
+ end
56
+ false
57
+ end
58
+
59
+ def fill
60
+ if num_columns < sheet.num_columns and
61
+ !filled?
62
+ filler_columns = sheet.num_columns - num_columns
63
+ filler_cell = self.new_cell('', span: filler_columns).to_html
64
+ end
65
+ end
66
+
67
+ def to_xls
68
+ <<-XML
69
+ <Row>
70
+ #{cells.map(&:to_xls).join}
71
+ </Row>
72
+ XML
73
+ end
74
+ end
@@ -0,0 +1,102 @@
1
+ class TableSetting::Sheet
2
+ attr_reader :stack
3
+ attr_accessor :name, :rows
4
+
5
+ @@counter = 0
6
+
7
+ def initialize(parent_stack = TableSetting::Stack.new, options = {})
8
+ @stack = parent_stack
9
+ @stack.sheets.push(self)
10
+ @rows = []
11
+
12
+ @@counter += 1
13
+ @name = options[:name] || "Sheet#{@@counter}"
14
+ end
15
+
16
+ def cells
17
+ list = []
18
+ rows.each do |row|
19
+ list.concat(row.cells)
20
+ end
21
+ list
22
+ end
23
+
24
+ def num_columns
25
+ max = 0
26
+ rows.each do |row|
27
+ if row.num_columns > max
28
+ max = row.num_columns
29
+ end
30
+ end
31
+ max
32
+ end
33
+
34
+ def style_column(number, options)
35
+ rows.each do |row|
36
+ row.fill
37
+ row.cells.select{|c| c.in_column?(number)}.map{|c| c.set_style(options)}
38
+ end
39
+ end
40
+
41
+ def spacer
42
+ self.new_row.new_cell('', span: 'all')
43
+ end
44
+
45
+ def new_row(options = {})
46
+ TableSetting::Row.new(self, options)
47
+ end
48
+
49
+ def to_csv
50
+ csv_string = CSV.generate do |csv|
51
+ rows.each do |row|
52
+ csv << row.to_a
53
+ end
54
+ end
55
+ csv_string
56
+ end
57
+
58
+ def to_html
59
+ <<-HTML
60
+ <style type="text/css">
61
+ #{css_styles}
62
+ </style>
63
+ <table class="grid-sheet">
64
+ #{rows.map(&:to_html).join("\n")}
65
+ </table>
66
+ HTML
67
+ end
68
+
69
+ def css_styles
70
+ signatures = {}
71
+ cells.each do |cell|
72
+ next if signatures[cell.style_name]
73
+ signatures[cell.style_name] = cell.style_css
74
+ end
75
+ # .grid-sheet td {background-color: #fff;}
76
+ # .grid-sheet tr:nth-child(odd) td {background-color: #eee;}
77
+ # .grid-sheet .bold {font-weight: bold;}
78
+ css = <<-CSS
79
+ .grid-sheet {background-color: #ddd; border-collapse: separate; border-spacing: 1px;}
80
+ .grid-sheet td {background-color: #fff;}
81
+ CSS
82
+ signatures.each do |signature_class, signature_css|
83
+ next if signature_css.empty?
84
+ css += "\n.grid-sheet td.#{signature_class} {#{signature_css}}"
85
+ end
86
+ css
87
+ end
88
+
89
+ def to_xls(stack_context = false)
90
+ if stack_context
91
+ <<-XML
92
+ <Worksheet ss:Name="#{self.name}">
93
+ <Table>
94
+ #{rows.map(&:to_xls).join}
95
+ </Table>
96
+ </Worksheet>
97
+ XML
98
+ else
99
+ stack.to_xls
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,56 @@
1
+ class TableSetting::Stack
2
+ attr_accessor :sheets
3
+
4
+ def initialize
5
+ @sheets = []
6
+ end
7
+
8
+ def cells
9
+ list = []
10
+ sheets.each do |sheet|
11
+ list += sheet.cells
12
+ end
13
+ list
14
+ end
15
+
16
+
17
+ def new_sheet(options = {})
18
+ TableSetting::Sheet.new(self, options)
19
+ end
20
+
21
+ def to_xls
22
+ xml = <<-XML
23
+ <?xml version="1.0"?>
24
+ <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
25
+ xmlns:o="urn:schemas-microsoft-com:office:office"
26
+ xmlns:x="urn:schemas-microsoft-com:office:excel"
27
+ xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
28
+ xmlns:html="http://www.w3.org/TR/REC-html40">
29
+ <Styles>
30
+ #{xls_styles}
31
+ </Styles>
32
+ #{sheets.map{|s| s.to_xls(true)}.join}
33
+ </Workbook>
34
+ XML
35
+ xml.strip!
36
+ end
37
+
38
+
39
+ def xls_styles
40
+ signatures = {}
41
+ cells.each do |cell|
42
+ next if signatures[cell.style_name]
43
+ signatures[cell.style_name] = cell.style_xls
44
+ end
45
+
46
+ xml = ''
47
+ signatures.each do |signature_class, signature_xls_style|
48
+ xml += <<-XML
49
+ <Style ss:ID="#{signature_class}">
50
+ #{signature_xls_style}
51
+ </Style>
52
+ XML
53
+ end
54
+ xml
55
+ end
56
+ end
@@ -0,0 +1,72 @@
1
+ class TableSetting::Style
2
+ attr_reader :bold, :size, :background, :color
3
+ def initialize(cell, options = {})
4
+ @bold = options[:bold] || cell.row.bold?
5
+ @size = options[:size] || cell.row.size
6
+ @background = options[:background] || cell.row.background
7
+ @color = options[:color] || cell.row.color
8
+ end
9
+
10
+ def update(options)
11
+ @bold = options[:bold] if options[:bold]
12
+ @background = options[:background] if options[:background]
13
+ @color = options[:color] if options[:color]
14
+ @size = options[:size] if options[:size]
15
+ end
16
+
17
+ def name
18
+ settings = {
19
+ bold: bold?,
20
+ background: background,
21
+ size: size,
22
+ color: color
23
+ }
24
+ "style-#{Digest::MD5.hexdigest(settings.to_s)[0..7]}"
25
+ end
26
+
27
+ def bold?
28
+ bold
29
+ end
30
+
31
+ def to_css
32
+ signature = ''
33
+ if bold?
34
+ signature += "font-weight: bold;"
35
+ end
36
+ if size
37
+ signature += "font-size: #{size};"
38
+ end
39
+ if background
40
+ signature += "background-color: #{background};"
41
+ end
42
+ if color
43
+ signature += "color: #{color};"
44
+ end
45
+ signature
46
+ end
47
+
48
+ def to_xls_xml
49
+ signature = ''
50
+ font_specs = {}
51
+ if bold?
52
+ font_specs["ss:Bold"] = 1
53
+ end
54
+ if size
55
+ end
56
+ if background
57
+ signature += %Q{<Interior ss:Color="#{background}" ss:Pattern="Solid"/>}
58
+ end
59
+ if color
60
+ font_specs["ss:Color"] = color
61
+ end
62
+ unless font_specs.empty?
63
+ spec_string = ''
64
+ font_specs.each do |key, value|
65
+ spec_string += %Q{#{key}="#{value}" }
66
+ end
67
+ signature += "<ss:Font #{spec_string} />"
68
+ end
69
+
70
+ signature
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ module TableSetting
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ require "table_setting/version"
2
+
3
+ require 'table_setting/cell'
4
+ require 'table_setting/column'
5
+ require 'table_setting/row'
6
+ require 'table_setting/sheet'
7
+ require 'table_setting/stack'
8
+ require 'table_setting/style'
9
+
10
+ module TableSetting
11
+
12
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'table_setting/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "table_setting"
8
+ spec.version = TableSetting::VERSION
9
+ spec.authors = ["Todd Gehman"]
10
+ spec.email = ["toddgehman@gmail.com"]
11
+ spec.description = %q{Ruby gem to create a data table that can be easily presented as HTML or Excel with the same human-friendly display styles applied to both. Unstyled formats like CSV are supported as well.}
12
+ spec.summary = %q{Data table exports with shared custom style settings.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: table_setting
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Todd Gehman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-08 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Ruby gem to create a data table that can be easily presented as HTML
42
+ or Excel with the same human-friendly display styles applied to both. Unstyled formats
43
+ like CSV are supported as well.
44
+ email:
45
+ - toddgehman@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE.txt
53
+ - README.md
54
+ - Rakefile
55
+ - lib/table_setting.rb
56
+ - lib/table_setting/cell.rb
57
+ - lib/table_setting/column.rb
58
+ - lib/table_setting/row.rb
59
+ - lib/table_setting/sheet.rb
60
+ - lib/table_setting/stack.rb
61
+ - lib/table_setting/style.rb
62
+ - lib/table_setting/version.rb
63
+ - table_setting.gemspec
64
+ homepage: ''
65
+ licenses:
66
+ - MIT
67
+ metadata: {}
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 2.0.3
85
+ signing_key:
86
+ specification_version: 4
87
+ summary: Data table exports with shared custom style settings.
88
+ test_files: []