spreet 0.0.3 → 0.0.4

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.
@@ -0,0 +1,54 @@
1
+ module Spreet
2
+
3
+ class Document
4
+ attr_reader :sheets
5
+ @@handlers = {}
6
+ @@associations = {}
7
+
8
+ def initialize(option={})
9
+ @sheets = Sheets.new(self)
10
+ end
11
+
12
+ def write(file, options={})
13
+ handler = self.class.extract_handler(file, options.delete(:format))
14
+ handler.write(self, file, options)
15
+ end
16
+
17
+ class << self
18
+
19
+ def register_handler(klass, name, options={})
20
+ if klass.respond_to?(:read) or klass.respond_to?(:write)
21
+ if name.is_a?(Symbol)
22
+ @@handlers[name] = klass # options.merge(:class=>klass)
23
+ elsif
24
+ raise ArgumentError.new("Name is invalid. Symbol expected, #{name.class.name} got.")
25
+ end
26
+ else
27
+ raise ArgumentError.new("Handler do not support :read or :write method.")
28
+ end
29
+ end
30
+
31
+ def read(file, options={})
32
+ handler = extract_handler(file, options.delete(:format))
33
+ return handler.read(file, options)
34
+ end
35
+
36
+ def extract_handler(file, handler_name=nil)
37
+ file_path = Pathname.new(file)
38
+ extension = file_path.extname.to_s[1..-1]
39
+ if !handler_name and extension.size > 0
40
+ handler_name = extension.to_sym
41
+ end
42
+ if @@handlers[handler_name]
43
+ return @@handlers[handler_name]
44
+ else
45
+ raise ArgumentError.new("No corresponding handler (#{handler_name.inspect}). Available: #{@@handlers.keys.collect{|k| k.inspect}.join(', ')}.")
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+
54
+ end
@@ -1,25 +1,9 @@
1
1
  module Spreet
2
-
3
- # Default handler
4
- class Handler
5
-
6
- def self.read(file, options={})
7
- raise NotImplementedError.new
8
- end
9
-
10
- def self.write(spreet, file, options={})
11
- raise NotImplementedError.new
12
- end
13
-
2
+ module Handlers
3
+ autoload :Base, 'spreet/handlers/base'
4
+ autoload :CSV, 'spreet/handlers/csv'
5
+ autoload :ExcelCSV, 'spreet/handlers/excel_csv'
6
+ autoload :OpenDocument, 'spreet/handlers/open_document'
14
7
  end
15
-
16
8
  end
17
9
 
18
- require 'spreet/handlers/csv'
19
- require 'spreet/handlers/open_document'
20
-
21
- Spreet::Document.register_handler Spreet::Handlers::CSV, :csv
22
- Spreet::Document.register_handler Spreet::Handlers::ExcelCSV, :xcsv
23
- # Spreet::Document.register_handler Spreet::Handlers::HTML, :html
24
- Spreet::Document.register_handler Spreet::Handlers::OpenDocument, :ods
25
- # Spreet::Document.register_handler Spreet::Handlers::PDF, :pdf
@@ -0,0 +1,19 @@
1
+ module Spreet
2
+ module Handlers
3
+
4
+ # Default handler
5
+ class Base
6
+
7
+ def self.read(file, options={})
8
+ raise NotImplementedError.new
9
+ end
10
+
11
+ def self.write(spreet, file, options={})
12
+ raise NotImplementedError.new
13
+ end
14
+
15
+ end
16
+
17
+
18
+ end
19
+ end
@@ -1,21 +1,15 @@
1
- # encoding: utf-8
2
- require 'fastercsv'
3
1
  require 'csv'
4
- require 'iconv'
5
2
 
6
3
  module Spreet
7
- # Universal CSV support
8
- CSV = (::CSV.const_defined?(:Reader) ? ::FasterCSV : ::CSV).freeze
9
-
10
4
  module Handlers
11
5
 
12
- class CSV < Spreet::Handler
6
+ class CSV < Spreet::Handlers::Base
13
7
 
14
8
  # Read a CSV file and create its Spreet document
15
9
  def self.read(file, options={})
16
10
  spreet = Spreet::Document.new
17
11
  sheet = spreet.sheets.add
18
- Spreet::CSV.foreach(file) do |row|
12
+ ::CSV.foreach(file) do |row|
19
13
  sheet.row *row
20
14
  end
21
15
  return spreet
@@ -25,7 +19,7 @@ module Spreet
25
19
  # Write a Spreet to a CSV file
26
20
  def self.write(spreet, file, options={})
27
21
  sheet = spreet.sheets[options[:sheet]||0]
28
- Spreet::CSV.open(file, "wb") do |csv|
22
+ ::CSV.open(file, "wb") do |csv|
29
23
  sheet.each_row do |row|
30
24
  csv << row.collect{|c| c.text}
31
25
  end
@@ -34,35 +28,6 @@ module Spreet
34
28
 
35
29
  end
36
30
 
37
- class ExcelCSV < Spreet::Handler
38
-
39
- # Read a CSV file and create its Spreet document
40
- def self.read(file, options={})
41
- spreet = Spreet::Document.new
42
- sheet = spreet.sheets.add
43
- options = {:col_sep=>';'}.merge(options)
44
- ic = Iconv.new('utf-8', 'cp1252')
45
- Spreet::CSV.foreach(file, options) do |row|
46
- sheet.row *(row.collect{|v| ic.iconv(v.to_s)})
47
- end
48
- return spreet
49
- end
50
-
51
-
52
- # Write a Spreet to a CSV file
53
- def self.write(spreet, file, options={})
54
- sheet = spreet.sheets[options[:sheet]||0]
55
- options = {:col_sep=>';'}.merge(options)
56
- ic = Iconv.new('cp1252', 'utf-8')
57
- Spreet::CSV.open(file, "wb", options) do |csv|
58
- sheet.each_row do |row|
59
- csv << row.collect{|c| ic.iconv(c.text)}
60
- end
61
- end
62
- end
63
-
64
- end
65
-
66
31
  end
67
32
  end
68
33
 
@@ -0,0 +1,36 @@
1
+ require 'csv'
2
+
3
+ module Spreet
4
+ module Handlers
5
+
6
+ class ExcelCSV < Spreet::Handlers::Base
7
+
8
+ # Read a CSV file and create its Spreet document
9
+ def self.read(file, options={})
10
+ spreet = Spreet::Document.new
11
+ sheet = spreet.sheets.add
12
+ options = {:col_sep=>';', :encoding => "CP1252"}.merge(options)
13
+ ::CSV.foreach(file, options) do |row|
14
+ sheet.row *(row.map{|v| v.to_s.encode('utf-8')}) # collect{|v| v.to_s.encode('cp1252')}
15
+ end
16
+ return spreet
17
+ end
18
+
19
+
20
+ # Write a Spreet to a CSV file
21
+ def self.write(spreet, file, options={})
22
+ sheet = spreet.sheets[options[:sheet]||0]
23
+ options = {:col_sep=>';', :encoding => "CP1252"}.merge(options)
24
+ ::CSV.open(file, "wb", options) do |csv|
25
+ sheet.each_row do |row|
26
+ csv << row.map(&:text)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+
@@ -1,10 +1,13 @@
1
1
  # encoding: utf-8
2
- require 'zip/zip'
2
+ require 'zip'
3
3
  require 'libxml'
4
+ require 'money'
5
+ require 'time'
6
+ require 'duration'
4
7
 
5
8
  module Spreet
6
9
  module Handlers
7
- class OpenDocument < Spreet::Handler
10
+ class OpenDocument < Spreet::Handlers::Base
8
11
  DATE_REGEXP = /\%./
9
12
  DATE_ELEMENTS = {
10
13
  "m" => "<number:month number:style=\"long\"/>",
@@ -62,7 +65,7 @@ module Spreet
62
65
 
63
66
  def self.read(file, options={})
64
67
  spreet = nil
65
- Zip::ZipFile.open(file) do |zile|
68
+ Zip::File.open(file) do |zile|
66
69
  # Check mime_type
67
70
  entry = zile.find_entry "mimetype"
68
71
  if entry.nil?
@@ -135,7 +138,7 @@ module Spreet
135
138
  elsif value_type == :currency
136
139
  value = cell.attributes.get_attribute_ns(XMLNS_OFFICE, "value").value
137
140
  currency = cell.attributes.get_attribute_ns(XMLNS_OFFICE, "currency").value
138
- sheet[x,y] = Money.new(value, currency)
141
+ sheet[x,y] = Money.new(value.to_f, currency)
139
142
  elsif value_type == :date
140
143
  value = cell.attributes.get_attribute_ns(XMLNS_OFFICE, "date-value").value
141
144
  if value.match(/\d{1,8}-\d{1,2}-\d{1,2}/)
@@ -202,9 +205,9 @@ module Spreet
202
205
  xml_escape << ".force_encoding('US-ASCII')" if xml_escape.respond_to?(:force_encoding)
203
206
  mime_type = MIME_ODS
204
207
  # name = #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')
205
- Zip::ZipOutputStream.open(file) do |zile|
208
+ Zip::OutputStream.open(file) do |zile|
206
209
  # MimeType in first place
207
- zile.put_next_entry('mimetype', nil, nil, Zip::ZipEntry::STORED)
210
+ zile.put_next_entry('mimetype', nil, nil, Zip::Entry::STORED)
208
211
  zile << mime_type
209
212
 
210
213
  # Manifest
@@ -0,0 +1,125 @@
1
+ module Spreet
2
+
3
+ class Sheet
4
+ attr_reader :document, :name, :columns
5
+ attr_accessor :current_row
6
+
7
+ def initialize(document, name=nil)
8
+ @document = document
9
+ self.name = name
10
+ raise ArgumentError.new("Must be a Document") unless document.is_a? Document
11
+ @current_row = 0
12
+ @cells = {} # BigArray::Cells.new
13
+ @bound = compute_bound
14
+ end
15
+
16
+ def name=(value)
17
+ unless value
18
+ value = (@document.sheets.count > 0 ? @document.sheets[-1].name.succ : "Sheet 1")
19
+ end
20
+ raise ArgumentError.new("Name of sheet must be given") if value.to_s.strip.size.zero?
21
+ if @document.sheets[value]
22
+ raise ArgumentError.new("Name of sheet must be unique")
23
+ end
24
+ @name = value
25
+ end
26
+
27
+ def next_row(increment = 1)
28
+ @current_row += increment
29
+ end
30
+
31
+ def previous_row(increment = 1)
32
+ @current_row -= increment
33
+ end
34
+
35
+ def [](*args)
36
+ coord = Coordinates.new(*args)
37
+ @cells[coord.to_i] ||= Cell.new(self, coord)
38
+ return @cells[coord.to_i]
39
+ end
40
+
41
+ def []=(*args)
42
+ value = args.delete_at(-1)
43
+ cell = self[*args]
44
+ cell.value = value
45
+ @updated = true
46
+ end
47
+
48
+ def row(*args)
49
+ options = {}
50
+ options = args.delete_at(-1) if args[-1].is_a? Hash
51
+ row = options[:row] || @current_row
52
+ args.each_index do |index|
53
+ self[index, row] = args[index]
54
+ end
55
+ next_row
56
+ end
57
+
58
+ def rows(index)
59
+ row = []
60
+ for i in 0..bound.x
61
+ row[i] = self[i, index]
62
+ end
63
+ return row
64
+ end
65
+
66
+ def each_row(&block)
67
+ for j in 0..bound.y
68
+ yield rows(j)
69
+ end
70
+ end
71
+
72
+ # Find or build cell
73
+ def cell(*args)
74
+ return c
75
+ end
76
+
77
+ def bound
78
+ if @updated
79
+ compute_bound
80
+ else
81
+ @bound
82
+ end
83
+ end
84
+
85
+ def remove!(coordinates)
86
+ raise ArgumentError.new("Must be a Coordinates") unless document.is_a?(Coordinates)
87
+ @cells.delete(coordinates.to_i)
88
+ @updated = true
89
+ end
90
+
91
+ # Moves the sheet to an other position in the list of sheets
92
+ def move_to(position)
93
+ @document.sheets.move_at(self, position)
94
+ end
95
+
96
+ # Moves the sheet higher in the list of sheets
97
+ def move_higher(increment=1)
98
+ @document.sheets.move(self, increment)
99
+ end
100
+
101
+ # Moves the sheet lower in the list of sheets
102
+ def move_lower(increment=1)
103
+ @document.sheets.move(self, -increment)
104
+ end
105
+
106
+ private
107
+
108
+ def compute_bound
109
+ bound = Coordinates.new(0,0)
110
+ for index, cell in @cells
111
+ # for cell in @cells.compact
112
+ unless cell.empty?
113
+ bound.x = cell.coordinates.x if cell.coordinates.x > bound.x
114
+ bound.y = cell.coordinates.y if cell.coordinates.y > bound.y
115
+ end
116
+ end
117
+ @updated = false
118
+ @bound = bound
119
+ return @bound
120
+ end
121
+
122
+ end
123
+
124
+
125
+ end
@@ -0,0 +1,63 @@
1
+ module Spreet
2
+
3
+ class Sheets
4
+
5
+ def initialize(document)
6
+ raise ArgumentError.new("Must be a Document") unless document.is_a?(Document)
7
+ @document = document
8
+ @array = []
9
+ end
10
+
11
+ def count
12
+ @array.size
13
+ end
14
+
15
+ def index(name_or_sheet)
16
+ if name_or_sheet.is_a? String
17
+ @array.each_index do |i|
18
+ return i if @array[i].name == name_or_sheet
19
+ end
20
+ elsif name_or_sheet.is_a? Integer
21
+ return (@array[name_or_sheet].nil? ? nil : name_or_sheet)
22
+ else
23
+ return @array.index(name_or_sheet)
24
+ end
25
+ end
26
+
27
+ def add(name=nil, position=-1)
28
+ sheet = Sheet.new(@document, name)
29
+ @array.insert(position, sheet)
30
+ return sheet
31
+ end
32
+
33
+ def [](sheet)
34
+ sheet = index(sheet)
35
+ return (sheet.is_a?(Integer) ? @array[sheet] : nil)
36
+ end
37
+
38
+ def remove(sheet)
39
+ @array.delete_at(index(sheet))
40
+ end
41
+
42
+ def move(sheet, shift=0)
43
+ position = index(sheet) + shift
44
+ position = 0 if position < 0
45
+ position = self.count-1 if position >= self.count
46
+ move_at(sheet, position)
47
+ end
48
+
49
+ def move_at(sheet, position=-1)
50
+ if i = index(sheet)
51
+ @array.insert(position, @array.delete_at(i))
52
+ end
53
+ end
54
+
55
+ def each(&block)
56
+ for item in @array
57
+ yield item
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end