workbook 0.1.1

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.
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