workbook 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/Gemfile.lock +22 -0
  4. data/Rakefile +12 -0
  5. data/lib/workbook/book.rb +122 -0
  6. data/lib/workbook/cell.rb +143 -0
  7. data/lib/workbook/format.rb +35 -0
  8. data/lib/workbook/modules/raw_objects_storage.rb +31 -0
  9. data/lib/workbook/modules/table_diff_sort.rb +140 -0
  10. data/lib/workbook/modules/type_parser.rb +97 -0
  11. data/lib/workbook/readers/csv_reader.rb +31 -0
  12. data/lib/workbook/readers/txt_reader.rb +17 -0
  13. data/lib/workbook/readers/xls_reader.rb +161 -0
  14. data/lib/workbook/row.rb +101 -0
  15. data/lib/workbook/sheet.rb +22 -0
  16. data/lib/workbook/table.rb +67 -0
  17. data/lib/workbook/template.rb +52 -0
  18. data/lib/workbook/writers/csv_table_writer.rb +23 -0
  19. data/lib/workbook/writers/xls_writer.rb +172 -0
  20. data/lib/workbook.rb +14 -0
  21. data/readme.markdown +99 -0
  22. data/test/artifacts/book_with_tabs_and_colours.xls +0 -0
  23. data/test/artifacts/complex_types.xls +0 -0
  24. data/test/artifacts/medewerkers.xls +0 -0
  25. data/test/artifacts/simple_csv.csv +4 -0
  26. data/test/artifacts/simple_excel_csv.csv +6 -0
  27. data/test/artifacts/simple_sheet.xls +0 -0
  28. data/test/artifacts/xls_with_txt_extension.txt +0 -0
  29. data/test/helper.rb +3 -0
  30. data/test/test_book.rb +56 -0
  31. data/test/test_cell.rb +82 -0
  32. data/test/test_format.rb +59 -0
  33. data/test/test_functional.rb +30 -0
  34. data/test/test_modules_table_diff_sort.rb +120 -0
  35. data/test/test_modules_type_parser.rb +53 -0
  36. data/test/test_readers_csv_reader.rb +37 -0
  37. data/test/test_readers_txt_reader.rb +49 -0
  38. data/test/test_readers_xls_reader.rb +26 -0
  39. data/test/test_row.rb +114 -0
  40. data/test/test_sheet.rb +26 -0
  41. data/test/test_table.rb +43 -0
  42. data/test/test_template.rb +24 -0
  43. data/test/test_writers_xls_writer.rb +37 -0
  44. data/workbook.gemspec +26 -0
  45. metadata +165 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ untitled document.xls
2
+ test.xls
3
+ compare*.xls
4
+ sharepoint_download_excel.*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in workbook.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ workbook (0.1.0)
5
+ fastercsv
6
+ rchardet (~> 1.3)
7
+ spreadsheet (>= 0.6.8)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ fastercsv (1.5.4)
13
+ rchardet (1.3)
14
+ ruby-ole (1.2.11.3)
15
+ spreadsheet (0.6.8)
16
+ ruby-ole (>= 1.0)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ workbook!
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :default => [:test]
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << "test"
10
+ t.test_files = FileList['test/test*.rb','test/writers/test*.rb']
11
+ t.verbose = false
12
+ end
@@ -0,0 +1,122 @@
1
+ require 'workbook/writers/xls_writer'
2
+ require 'workbook/readers/xls_reader'
3
+ require 'workbook/readers/csv_reader'
4
+ require 'workbook/readers/txt_reader'
5
+ require 'rchardet'
6
+
7
+ module Workbook
8
+ class Book < Array
9
+ include Workbook::Writers::XlsWriter
10
+ include Workbook::Readers::XlsReader
11
+ include Workbook::Readers::CsvReader
12
+ include Workbook::Readers::TxtReader
13
+
14
+ attr_accessor :title
15
+ attr_accessor :template
16
+ attr_accessor :default_rewrite_header
17
+
18
+ # @param [Workbook::Sheet, Array] Create a new workbook based on an existing sheet, or initialize a sheet based on the array
19
+ # @return [Workbook::Book]
20
+ def initialize sheet=Workbook::Sheet.new([], self, options={})
21
+ if sheet.is_a? Workbook::Sheet
22
+ push sheet
23
+ else
24
+ push Workbook::Sheet.new(sheet, self, options)
25
+ end
26
+ end
27
+
28
+ # @return [Workbook::Format] returns the template describing how the document should be/is formatted
29
+ def template
30
+ @template ||= Workbook::Template.new
31
+ end
32
+
33
+ # @param [Workbook::Format] a template describing how the document should be/is formatted
34
+ def template= template
35
+ raise ArgumentError, "format should be a Workboot::Format" unless template.is_a? Workbook::Template
36
+ @template = template
37
+ end
38
+
39
+ # @return [String] the title of the workbook
40
+ def title
41
+ @title ? @title : "untitled document"
42
+ end
43
+
44
+ def push sheet=Workbook::Sheet.new
45
+ super(sheet)
46
+ end
47
+
48
+ # @return [Workbook::Sheet] The first sheet, and creates an empty one if one doesn't exists
49
+ def sheet
50
+ push Workbook::Sheet.new unless first
51
+ first
52
+ end
53
+
54
+ def has_contents?
55
+ sheet.has_contents?
56
+ end
57
+
58
+ # Loads an external file into an existing worbook
59
+ # @param [String] a string with a reference to the file to be opened
60
+ # @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
61
+ def open filename, ext=nil
62
+ ext = file_extension(filename) unless ext
63
+ if ['txt','csv','xml'].include?(ext)
64
+ open_text filename, ext
65
+ else
66
+ open_binary filename, ext
67
+ end
68
+ end
69
+
70
+ # Open the file in binary, read-only mode, do not read it, but pas it throug to the extension determined loaded
71
+ # @param [String] a string with a reference to the file to be opened
72
+ # @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
73
+ def open_binary filename, ext=nil
74
+ ext = file_extension(filename) unless ext
75
+ f = File.open(filename,'rb')
76
+ send("load_#{ext}".to_sym,f)
77
+ end
78
+
79
+ # Open the file in non-binary, read-only mode, read it and parse it to UTF-8
80
+ # @param [String] a string with a reference to the file to be opened
81
+ # @param [String] an optional string enforcing a certain parser (based on the file extension, e.g. 'txt', 'csv' or 'xls')
82
+ def open_text filename, ext=nil
83
+ ext = file_extension(filename) unless ext
84
+ f = File.open(filename,'r')
85
+ t = f.read
86
+ detected_encoding = CharDet.detect(t)['encoding']
87
+ t = Iconv.conv("UTF-8//TRANSLIT//IGNORE",detected_encoding,t)
88
+ send("load_#{ext}".to_sym,t)
89
+ end
90
+
91
+ # @param [String] The full filename, or path
92
+ # @return [String] The file extension
93
+ def file_extension(filename)
94
+ File.extname(filename).gsub('.','').downcase if filename
95
+ end
96
+
97
+ # Create an instance from a file, using open.
98
+ # @return [Workbook::Book] A new instance, based on the filename
99
+ def self.open filename, ext=nil
100
+ wb = self.new
101
+ wb.open filename, ext
102
+ return wb
103
+ end
104
+
105
+ def create_or_open_sheet_at index
106
+ s = self[index]
107
+ s = self[index] = Workbook::Sheet.new if s == nil
108
+ s.book = self
109
+ s
110
+ end
111
+
112
+ def sort
113
+ raise Exception("Books can't be sorted")
114
+ end
115
+
116
+ def default_rewrite_header?
117
+ return true if default_rewrite_header.nil?
118
+ default_rewrite_header
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+
3
+ require 'workbook/modules/type_parser'
4
+
5
+ module Workbook
6
+ class Cell
7
+ include Workbook::Modules::TypeParser
8
+
9
+ attr_accessor :value
10
+ attr_accessor :format
11
+ attr_accessor :formula
12
+
13
+ # Note that these types are sorted by 'importance'
14
+ VALID_TYPES = [Numeric,String,Time,Date,TrueClass,FalseClass,NilClass]
15
+
16
+ def valid_value? value
17
+ valid_type = false
18
+ VALID_TYPES.each {|t| return true if value.is_a? t}
19
+ valid_type
20
+ end
21
+
22
+ def initialize value=nil, options={}
23
+ if valid_value? value
24
+ format = options[:format]
25
+ @value = value
26
+ else
27
+ raise ArgumentError, "value should be of a primitive type, e.g. a string, or an integer, not a #{value.class} (is_a? [TrueClass,FalseClass,Date,Time,Numeric,String, NilClass])"
28
+ end
29
+ end
30
+
31
+ def format= f
32
+ if f.is_a? Workbook::Format
33
+ @format = f
34
+ elsif f.is_a? Hash
35
+ @format = Workbook::Format.new(f)
36
+ elsif f.class == NilClass
37
+ @format = Workbook::Format.new
38
+ end
39
+ end
40
+
41
+ def format
42
+ @format ||= Workbook::Format.new
43
+ end
44
+
45
+ def ==(other)
46
+ if other.is_a? Cell
47
+ other.value == self.value
48
+ else
49
+ other == self.value
50
+ end
51
+ end
52
+
53
+ def nil?
54
+ return value.nil?
55
+ end
56
+
57
+ def to_sym
58
+ #mb_chars.normalize(:kd).
59
+ v = nil
60
+ if value
61
+ v = value.to_s.downcase
62
+ v = v.gsub(' (j/?/leeg)','').gsub(/dd-mm-(.*)/,'').gsub(/\ja\/nee/,'').gsub(/\(\)/,'').gsub(/[\(\)]+/, '')
63
+ v = v.strip.gsub(/(\.|\?|,|\=)/,'').
64
+ gsub('$','').
65
+ gsub(/\&/,'en').
66
+ gsub(/\+/,'_plus_').
67
+ gsub(/\s/, "_").
68
+ gsub('–_','').
69
+ gsub('-_','').
70
+ gsub('+_','').
71
+ gsub('/_','_').
72
+ gsub('/','_').
73
+ gsub('__','_').
74
+ gsub('-','')
75
+
76
+ accents = {
77
+ ['á','à','â','ä','ã'] => 'a',
78
+ ['Ã','Ä','Â','À','�?'] => 'A',
79
+ ['é','è','ê','ë'] => 'e',
80
+ ['Ë','É','È','Ê'] => 'E',
81
+ ['í','ì','î','ï'] => 'i',
82
+ ['�?','Î','Ì','�?'] => 'I',
83
+ ['ó','ò','ô','ö','õ'] => 'o',
84
+ ['Õ','Ö','Ô','Ò','Ó'] => 'O',
85
+ ['ú','ù','û','ü'] => 'u',
86
+ ['Ú','Û','Ù','Ü'] => 'U',
87
+ ['ç'] => 'c', ['Ç'] => 'C',
88
+ ['ñ'] => 'n', ['Ñ'] => 'N'
89
+ }
90
+ accents.each do |ac,rep|
91
+ ac.each do |s|
92
+ v = v.gsub(s, rep)
93
+ end
94
+ end
95
+
96
+ v = v.gsub(/[^\x00-\x7F]/n,'').downcase.to_sym
97
+ end
98
+ v
99
+ end
100
+
101
+ def <=> other
102
+ rv = nil
103
+ begin
104
+ rv = self.value <=> other.value
105
+ rescue NoMethodError => e
106
+ rv = compare_on_class other
107
+ end
108
+ if rv == nil
109
+ rv = compare_on_class other
110
+ end
111
+ return rv
112
+
113
+ end
114
+
115
+ def compare_on_class other
116
+ other_value = nil
117
+ other_value = other.value if other
118
+ self_value = importance_of_class self.value
119
+ other_value = importance_of_class other_value
120
+ self_value <=> other_value
121
+ end
122
+
123
+ def importance_of_class value
124
+ VALID_TYPES.each_with_index do |c,i|
125
+ return i if value.is_a? c
126
+ end
127
+ return nil
128
+ end
129
+
130
+ def inspect
131
+ "<Workbook::Cell @value=#{value}>"
132
+ end
133
+
134
+ def to_s
135
+ if (value.is_a? Date or value.is_a? Time) and format[:number_format]
136
+ value.strftime(format[:number_format])
137
+ else
138
+ value.to_s
139
+ end
140
+ end
141
+
142
+ end
143
+ end
@@ -0,0 +1,35 @@
1
+ require 'workbook/modules/raw_objects_storage'
2
+
3
+ module Workbook
4
+ class Format < Hash
5
+ include Workbook::Modules::RawObjectsStorage
6
+ alias_method :merge_hash, :merge
7
+
8
+ attr_accessor :name
9
+
10
+ def initialize options={}
11
+ options.each {|k,v| self[k]=v}
12
+ end
13
+
14
+ def has_background_color? color=:any
15
+ if self[:background_color]
16
+ return (self[:background_color].downcase==color.to_s.downcase or (!(self[:background_color]==nil or (self[:background_color].is_a? String and (self[:background_color].downcase=='#ffffff' or self[:background_color]=='#000000'))) and color==:any))
17
+ else
18
+ return false
19
+ end
20
+ end
21
+
22
+ # Returns a string that can be used as inline cell styling (e.g. `<td style="<%=cell.format.to_css%>"><%=cell%></td>`)
23
+ def to_css
24
+ css_parts = []
25
+ css_parts.push("background: #{self[:background_color].to_s} #{self[:background].to_s}".strip) if self[:background] or self[:background_color]
26
+ css_parts.push("color: #{self[:color].to_s}") if self[:color]
27
+ css_parts.join("; ")
28
+ end
29
+
30
+ def merge(a)
31
+ self.remove_all_raws!
32
+ self.merge_hash(a)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ module Workbook
2
+ module Modules
3
+ module RawObjectsStorage
4
+
5
+ # A raw is a 'raw' object, representing a workbook, or cell, or whatever... in a particular format (defined by its class)
6
+ def add_raw raw_object
7
+ raws[raw_object.class]=raw_object
8
+ end
9
+
10
+ # Returns true if there is a template for a certain class, otherwise false
11
+ def has_raw_for? raw_object_class
12
+ raws.each { |tc,t| return true if tc == raw_object_class}
13
+ return false
14
+ end
15
+
16
+ def return_raw_for raw_object_class
17
+ raws.each { |tc,t| return t if tc == raw_object_class}
18
+ return nil
19
+ end
20
+
21
+ def remove_all_raws!
22
+ @raws = {}
23
+ end
24
+
25
+ def raws
26
+ @raws = {} unless defined? @raws
27
+ @raws
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,140 @@
1
+ # Adds diffing and sorting functions
2
+ module Workbook
3
+ module Modules
4
+ module TableDiffSort
5
+ # create an overview of the differences between itself with another table, returns a book with a single sheet and table (containing the diffs)
6
+ def diff other, options={:sort=>true,:ignore_headers=>false}
7
+
8
+ aligned = align(other, options)
9
+ aself = aligned[:self]
10
+ aother = aligned[:other]
11
+ iteration_cols = []
12
+ if options[:ignore_headers]
13
+ iteration_cols = [aother.first.count,aself.first.count].max.times.collect
14
+ else
15
+ iteration_cols = (aother.header.to_symbols+aother.header.to_symbols).uniq
16
+ end
17
+ diff_table = diff_template.sheet.table
18
+ maxri = (aself.count-1)
19
+ for ri in 0..maxri do
20
+ row = diff_table[ri]
21
+ row = diff_table[ri] = Workbook::Row.new(nil, diff_table)
22
+ srow = aself[ri]
23
+ orow = aother[ri]
24
+
25
+ iteration_cols.each_with_index do |ch, ci|
26
+ scell = srow[ch]
27
+ ocell = orow[ch]
28
+ dcell = scell.nil? ? Workbook::Cell.new(nil) : scell
29
+ if (scell == ocell)
30
+ dcell.format = scell.format if scell
31
+ elsif scell.nil?
32
+ dcell = Workbook::Cell.new "(was: #{ocell.to_s})"
33
+ dcell.format = diff_template.template.create_or_find_format_by 'destroyed'
34
+ elsif ocell.nil?
35
+ dcell = scell.clone
36
+ fmt = scell.nil? ? :default : scell.format[:number_format]
37
+ f = diff_template.template.create_or_find_format_by 'created', fmt
38
+ f[:number_format] = scell.format[:number_format]
39
+ dcell.format = f
40
+ elsif scell != ocell
41
+ dcell = Workbook::Cell.new "#{scell.to_s} (was: #{ocell.to_s})"
42
+ f = diff_template.template.create_or_find_format_by 'updated'
43
+ dcell.format = f
44
+ end
45
+
46
+ row[ci]=dcell
47
+ end
48
+ end
49
+ if !options[:ignore_headers]
50
+ diff_table[0].format = diff_template.template.create_or_find_format_by 'header'
51
+ end
52
+
53
+ diff_template
54
+ end
55
+
56
+ def diff_template
57
+ return @diff_template if @diff_template
58
+ diffbook = Workbook::Book.new
59
+ difftable = diffbook.sheet.table
60
+ template = diffbook.template
61
+ f = template.create_or_find_format_by 'destroyed'
62
+ f[:background_color]=:red
63
+ f = template.create_or_find_format_by 'updated'
64
+ f[:background_color]=:yellow
65
+ f = template.create_or_find_format_by 'created'
66
+ f[:background_color]=:lime
67
+ f = template.create_or_find_format_by 'header'
68
+ f[:rotation] = 72
69
+ f[:font_weight] = :bold
70
+ f[:height] = 80
71
+ @diff_template = diffbook
72
+ return diffbook
73
+ end
74
+
75
+ # aligns itself with another table, used by diff
76
+ def align other, options={:sort=>true,:ignore_headers=>false}
77
+
78
+ options = {:sort=>true,:ignore_headers=>false}.merge(options)
79
+
80
+ iteration_cols = nil
81
+ sother = other.clone.remove_empty_lines!
82
+ sself = self.clone.remove_empty_lines!
83
+
84
+ if options[:ignore_headers]
85
+ sother.header = false
86
+ sself.header = false
87
+ end
88
+
89
+ sother = options[:sort] ? Workbook::Table.new(sother.sort) : sother
90
+ sself = options[:sort] ? Workbook::Table.new(sself.sort) : sself
91
+
92
+ iteration_rows = [sother.count,sself.count].max.times.collect
93
+
94
+ row_index = 0
95
+ while row_index < [sother.count,sself.count].max and row_index < other.count+self.count do
96
+ row_index = align_row(sself, sother, row_index)
97
+ end
98
+
99
+ {:self=>sself, :other=>sother}
100
+ end
101
+
102
+ # for use in the align 'while' loop
103
+ def align_row sself, sother, row_index
104
+ asd = 0
105
+ if sself[row_index] and sother[row_index]
106
+ asd = sself[row_index].key <=> sother[row_index].key
107
+ elsif sself[row_index]
108
+ asd = -1
109
+ elsif sother[row_index]
110
+ asd = 1
111
+ end
112
+ if asd == -1 and insert_placeholder?(sother, sself, row_index)
113
+ sother.insert row_index, placeholder_row
114
+ row_index -=1
115
+ elsif asd == 1 and insert_placeholder?(sother, sself, row_index)
116
+ sself.insert row_index, placeholder_row
117
+ row_index -=1
118
+ end
119
+
120
+ row_index += 1
121
+ end
122
+
123
+ def insert_placeholder? sother, sself, row_index
124
+ (sother[row_index].nil? or !sother[row_index].placeholder?) and
125
+ (sself[row_index].nil? or !sself[row_index].placeholder?)
126
+ end
127
+
128
+ # returns a placeholder row, for internal use only
129
+ def placeholder_row
130
+ if @placeholder_row != nil
131
+ return @placeholder_row
132
+ else
133
+ @placeholder_row = Workbook::Row.new [nil]
134
+ placeholder_row.placeholder = true
135
+ return @placeholder_row
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,97 @@
1
+ module Workbook
2
+ module Modules
3
+ module TypeParser
4
+ def strip_win_chars csv_raw
5
+ csv_raw.gsub(/(\n\r|\r\n|\r)/,"\n")
6
+ end
7
+
8
+ def string_parsers
9
+ @string_parsers ||= [:string_cleaner,:string_nil_converter,:string_integer_converter,:string_boolean_converter]
10
+ end
11
+
12
+ def string_parsers= arr
13
+ @string_parsers = arr
14
+ end
15
+
16
+ def string_parsers_as_procs
17
+ string_parsers.collect{|c| c.is_a?(Proc) ? c : self.send(c)}
18
+ end
19
+
20
+ def parse options={}
21
+ options = {:detect_date=>false}.merge(options)
22
+ string_parsers.push :string_optimistic_date_converter if options[:detect_date]
23
+ v = value
24
+ string_parsers_as_procs.each do |p|
25
+ if v.is_a? String
26
+ v = p.call(v)
27
+ end
28
+ end
29
+ v
30
+ end
31
+
32
+ def parse! options={}
33
+ self.value = parse(options)
34
+ end
35
+
36
+ def clean! options={}
37
+ parse! options
38
+ end
39
+
40
+ def string_cleaner
41
+ proc do |v|
42
+ v = v.strip
43
+ v.gsub('mailto:','')
44
+ end
45
+ end
46
+
47
+ def string_nil_converter
48
+ proc do |v|
49
+ return v == "" ? nil : v
50
+ end
51
+ end
52
+
53
+ def string_integer_converter
54
+ proc do |v|
55
+ if v.to_i.to_s == v
56
+ return v.to_i
57
+ else
58
+ v
59
+ end
60
+ end
61
+ end
62
+
63
+ def string_optimistic_date_converter
64
+ proc do |v|
65
+ rv = v
66
+ starts_with_nr = v.chars.first.to_i.to_s == v.chars.first #it should at least start with a number...
67
+ no_spaced_dash = v.to_s.match(" - ") ? false : true
68
+ normal_date_length = v.to_s.length <= 25
69
+ if no_spaced_dash and starts_with_nr and normal_date_length
70
+ begin
71
+ rv = (v.length > 10) ? DateTime.parse(v) : Date.parse(v)
72
+ rescue ArgumentError
73
+ rv = v
74
+ end
75
+ begin
76
+ rv = Date.parse(v.to_i.to_s) == rv ? v : rv # disqualify is it is only based on the first number
77
+ rescue ArgumentError
78
+ end
79
+ end
80
+ rv
81
+ end
82
+ end
83
+
84
+ def string_boolean_converter
85
+ proc do |v|
86
+ dv = v.downcase
87
+ if dv == "true"
88
+ return true
89
+ elsif dv == "false"
90
+ return false
91
+ end
92
+ v
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,31 @@
1
+ require 'faster_csv'
2
+
3
+ module Workbook
4
+ module Readers
5
+ module CsvReader
6
+ def load_csv text
7
+ csv = text
8
+ parse_csv csv
9
+ end
10
+
11
+ def parse_csv csv_raw
12
+ custom_date_converter = Workbook::Cell.new.string_optimistic_date_converter
13
+ converters = [:float,:integer,:date,:date_time,custom_date_converter]
14
+ csv=nil
15
+ begin
16
+ csv = FasterCSV.parse(csv_raw,{:converters=>converters})
17
+ rescue
18
+ # we're going to have another shot at it...
19
+ end
20
+
21
+ if csv==nil or csv[0].count == 1
22
+ csv_excel = FasterCSV.parse(csv_raw,{:converters=>converters,:col_sep=>';'})
23
+ csv = csv_excel if csv_excel[0].count > 1
24
+ end
25
+
26
+ self[0]=Workbook::Sheet.new(csv,self) unless sheet.has_contents?
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'faster_csv'
2
+ module Workbook
3
+ module Readers
4
+ module TxtReader
5
+ def load_txt text
6
+ csv = text
7
+ parse_txt csv
8
+ end
9
+
10
+ def parse_txt csv_raw
11
+ csv = []
12
+ csv_raw.split("\n").each {|l| csv << FasterCSV.parse_line(l,{:col_sep=>"\t"});nil}
13
+ self[0]=Workbook::Sheet.new(csv,self,{:parse_cells_on_batch_creation=>true, :cell_parse_options=>{:detect_date=>true}}) unless sheet.has_contents?
14
+ end
15
+ end
16
+ end
17
+ end