spreet 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ Gemfile.lock
2
+ *~
3
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :gemcutter
2
+
3
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Brice Texier
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ = Spreet
2
+
3
+ Universal handler for spr[eadsh]eets.
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
11
+
12
+ # Import all rake files
13
+ for rakefile in Dir.glob('lib/tasks/*.rake')
14
+ import(rakefile)
15
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,368 @@
1
+ # encoding: utf-8
2
+
3
+ module Spreet
4
+
5
+ module VERSION
6
+ version = nil
7
+ File.open("VERSION") {|f| version = f.read.split('.')}
8
+ MAJOR = version[0].to_i.freeze
9
+ MINOR = version[1].to_i.freeze
10
+ TINY = version[2].to_i.freeze
11
+ PATCH = TINY.freeze
12
+ PRE = version[3].freeze
13
+ STRING = version.freeze
14
+ end
15
+
16
+
17
+ class Coordinates
18
+ # Limit coordinates x and y in 0..65535 but coordinates are in one integer of 32 bits
19
+ CPU_SEMI_WIDTH = 16 # ((RUBY_PLATFORM.match(/^[^\-]*[^\-0-9]64/) ? 64 : 32) / 2).freeze
20
+ Y_FILTER = ((1 << CPU_SEMI_WIDTH) - 1).freeze
21
+
22
+ BASE_26_BEF = "0123456789abcdefghijklmnop"
23
+ BASE_26_AFT = "abcdefghijklmnopqrstuvwxyz"
24
+
25
+ attr_accessor :x, :y
26
+ def initialize(*args)
27
+ value = (args.size == 1 ? args[0] : args)
28
+ @x, @y = 0, 0
29
+ if value.is_a? String
30
+ if value.downcase.match(/^[a-z]+[0-9]+$/)
31
+ value = value.downcase.split(/([A-Z]+|[0-9]+)/).delete_if{|x| x.size.zero?}
32
+ @x, @y = value[0].tr(BASE_26_AFT, BASE_26_BEF).to_i(26), value[1].to_i(10)-1
33
+ elsif value.downcase.match(/^[0-9]+[^0-9]+[0-9]+$/)
34
+ value = value.downcase.split(/[^0-9]+/)
35
+ @x, @y = value[0].to_i(10), value[1].to_i(10)
36
+ end
37
+ elsif value.is_a? Integer
38
+ @x, @y = (value >> CPU_SEMI_WIDTH), value & Y_FILTER
39
+ elsif value.is_a? Coordinates
40
+ @x, @y = value.x, value.y
41
+ elsif value.is_a? Array
42
+ @x, @y = value[0].to_i, value[1].to_i
43
+ elsif value.is_a? Hash
44
+ @x, @y = value[:x] || value[:column] || 0, value[:y] || value[:row] || 0
45
+ end
46
+ end
47
+
48
+ def to_s
49
+ @x.to_s(26).tr(BASE_26_BEF, BASE_26_AFT).upcase+(@y+1).to_s(10)
50
+ end
51
+
52
+ def to_a
53
+ [@x, @y]
54
+ end
55
+
56
+ def to_hash
57
+ {:x=>@x, :y=>@y}
58
+ end
59
+
60
+ def to_i
61
+ (@x << CPU_SEMI_WIDTH) + @y
62
+ end
63
+
64
+ def ==(other_coordinate)
65
+ other_coordinate.x == self.x and other_coordinate.y == self.y
66
+ end
67
+
68
+ def <=>(other_coordinate)
69
+ self.to_i <=> other_coordinate.to_i
70
+ end
71
+ end
72
+
73
+ # Represents a cell in a sheet
74
+ class Cell
75
+ attr_reader :text, :value, :type, :sheet, :coordinates
76
+
77
+ def initialize(sheet, *args)
78
+ @sheet = sheet
79
+ @coordinates = Coordinates.new(*args)
80
+ self.value = nil
81
+ @empty = true
82
+ end
83
+
84
+ def value=(val)
85
+ @value = val
86
+ @type = determine_type
87
+ @text = val.to_s
88
+ @empty = false
89
+ end
90
+
91
+ def empty?
92
+ @empty
93
+ end
94
+
95
+ def clear!
96
+ self.value = nil
97
+ @empty = true
98
+ end
99
+
100
+ def remove!
101
+ @sheet.remove(self.coordinates)
102
+ end
103
+
104
+ def <=>(other_cell)
105
+ self.coordinates <=> other_cell.coordinates
106
+ end
107
+
108
+ private
109
+
110
+ def determine_type
111
+ if value.is_a? Date
112
+ :date
113
+ elsif value.is_a? Integer
114
+ :integer
115
+ elsif value.is_a? Numeric
116
+ :decimal
117
+ elsif value.is_a? DateTime
118
+ :datetime
119
+ elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
120
+ :boolean
121
+ elsif value.nil?
122
+ :null
123
+ else
124
+ :string
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+
131
+ class Sheet
132
+ attr_reader :document, :name, :columns
133
+ attr_accessor :current_row
134
+
135
+ def initialize(document, name=nil)
136
+ @document = document
137
+ self.name = name
138
+ raise ArgumentError.new("Must be a Document") unless document.is_a? Document
139
+ @current_row = 0
140
+ @cells = {}
141
+ end
142
+
143
+ def name=(value)
144
+ unless value
145
+ value = (@document.sheets.count > 0 ? @document.sheets[-1].name.succ : "Sheet 1")
146
+ end
147
+ raise ArgumentError.new("Name of sheet must be given") if value.to_s.strip.size.zero?
148
+ if @document.sheets[value]
149
+ raise ArgumentError.new("Name of sheet must be unique")
150
+ end
151
+ @name = value
152
+ end
153
+
154
+ def cells
155
+ @cells.delete_if{|k,v| v.empty?}
156
+ @cells.values
157
+ end
158
+
159
+ def next_row(increment = 1)
160
+ @current_row += increment
161
+ end
162
+
163
+ def previous_row(increment = 1)
164
+ @current_row -= increment
165
+ end
166
+
167
+ def [](*args)
168
+ coord = Coordinates.new(*args)
169
+ @cells[coord.to_i] ||= Cell.new(self, coord)
170
+ return @cells[coord.to_i]
171
+ end
172
+
173
+ def []=(*args)
174
+ value = args.delete_at(-1)
175
+ cell = self[*args]
176
+ cell.value = value
177
+ @bound = compute_bound
178
+ end
179
+
180
+ def row(*args)
181
+ options = {}
182
+ options = args.delete_at(-1) if args[-1].is_a? Hash
183
+ row = options[:row] || @current_row
184
+ args.each_index do |index|
185
+ self[index, row] = args[index]
186
+ end
187
+ next_row
188
+ end
189
+
190
+ def each_row(&block)
191
+ for j in 0..bound.y
192
+ row = []
193
+ for i in 0..bound.x
194
+ row[i] = self[i, j]
195
+ end
196
+ yield row
197
+ end
198
+ end
199
+
200
+ # Find or build cell
201
+ def cell(*args)
202
+ return c
203
+ end
204
+
205
+ def bound
206
+ @bound
207
+ end
208
+
209
+ def remove!(coordinates)
210
+ raise ArgumentError.new("Must be a Coordinates") unless document.is_a?(Coordinates)
211
+ @cells.delete(coordinates.to_i)
212
+ @bound = compute_bound
213
+ end
214
+
215
+ # Moves the sheet to an other position in the list of sheets
216
+ def move_to(position)
217
+ @document.sheets.move_at(self, position)
218
+ end
219
+
220
+ # Moves the sheet higher in the list of sheets
221
+ def move_higher(increment=1)
222
+ @document.sheets.move(self, increment)
223
+ end
224
+
225
+ # Moves the sheet lower in the list of sheets
226
+ def move_lower(increment=1)
227
+ @document.sheets.move(self, -increment)
228
+ end
229
+
230
+ private
231
+
232
+ def compute_bound
233
+ bound = Coordinates.new
234
+ for id, cell in @cells
235
+ unless cell.empty?
236
+ bound.x = cell.coordinates.x if cell.coordinates.x > bound.x
237
+ bound.y = cell.coordinates.y if cell.coordinates.x > bound.y
238
+ end
239
+ end
240
+ return bound
241
+ end
242
+
243
+ end
244
+
245
+
246
+ class Sheets
247
+
248
+ def initialize(document)
249
+ raise ArgumentError.new("Must be a Document") unless document.is_a?(Document)
250
+ @document = document
251
+ @array = []
252
+ end
253
+
254
+ def count
255
+ @array.size
256
+ end
257
+
258
+ def index(name_or_sheet)
259
+ if name_or_sheet.is_a? String
260
+ @array.each_index do |i|
261
+ return i if @array[i].name == name_or_sheet
262
+ end
263
+ elsif name_or_sheet.is_a? Integer
264
+ return (@array[name_or_sheet].nil? ? nil : name_or_sheet)
265
+ else
266
+ return @array.index(name_or_sheet)
267
+ end
268
+ end
269
+
270
+ def add(name=nil, position=-1)
271
+ sheet = Sheet.new(@document, name)
272
+ @array.insert(position, sheet)
273
+ return sheet
274
+ end
275
+
276
+ def [](sheet)
277
+ sheet = index(sheet)
278
+ return (sheet.is_a?(Integer) ? @array[sheet] : nil)
279
+ end
280
+
281
+ def remove(sheet)
282
+ @array.delete(sheet)
283
+ end
284
+
285
+ def move(sheet, shift=0)
286
+ move_at(sheet, index(sheet) + shift)
287
+ end
288
+
289
+ def move_at(sheet, position=-1)
290
+ if i = index(sheet)
291
+ @array.insert(position, @array.delete_at(i))
292
+ end
293
+ end
294
+
295
+ def each(&block)
296
+ for item in @array
297
+ yield item
298
+ end
299
+ end
300
+
301
+ end
302
+
303
+
304
+ class Document
305
+ attr_reader :sheets
306
+ @@handlers = {}
307
+ @@associations = {}
308
+
309
+ def initialize(option={})
310
+ @sheets = Sheets.new(self)
311
+ end
312
+
313
+ def to_term
314
+ text = "Spreet (#{@sheets.count}):\n"
315
+ for sheet in @sheets
316
+ text << " - #{sheet.name}:\n"
317
+ for cell in sheet.cells.sort
318
+ text << " - #{cell.coordinates.to_s}: #{cell.text.inspect}\n"
319
+ end
320
+ end
321
+ return text
322
+ end
323
+
324
+ def write(file, options={})
325
+ handler = self.class.extract_handler(file, options.delete(:format))
326
+ handler.write(self, file, options)
327
+ end
328
+
329
+ class << self
330
+
331
+ def register_handler(klass, name, options={})
332
+ if klass.respond_to?(:read) or klass.respond_to?(:write)
333
+ if name.is_a?(Symbol)
334
+ @@handlers[name] = klass # options.merge(:class=>klass)
335
+ elsif
336
+ raise ArgumentError.new("Name is invalid. Symbol expected, #{name.class.name} got.")
337
+ end
338
+ else
339
+ raise ArgumentError.new("Handler do not support :read or :write method.")
340
+ end
341
+ end
342
+
343
+ def read(file, options={})
344
+ handler = extract_handler(file, options.delete(:format))
345
+ return handler.read(file, options)
346
+ end
347
+
348
+ def extract_handler(file, handler_name=nil)
349
+ file_path = Pathname.new(file)
350
+ extension = file_path.extname.to_s[1..-1]
351
+ if !handler_name and extension.size > 0
352
+ handler_name = extension.to_sym
353
+ end
354
+ if @@handlers[handler_name]
355
+ return @@handlers[handler_name]
356
+ else
357
+ raise ArgumentError.new("No corresponding handler (#{handler_name.inspect}). Available: #{@@handlers.keys.collect{|k| k.inspect}.join(', ')}.")
358
+ end
359
+ end
360
+
361
+ end
362
+
363
+
364
+ end
365
+
366
+ end
367
+
368
+ require 'spreet/handlers'
@@ -0,0 +1,25 @@
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
+
14
+ end
15
+
16
+ end
17
+
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,68 @@
1
+ # encoding: utf-8
2
+ require 'csv'
3
+ require 'iconv'
4
+
5
+ module Spreet
6
+ # Universal CSV support
7
+ CSV = (::CSV.const_defined?(:Reader) ? ::FasterCSV : ::CSV).freeze
8
+
9
+ module Handlers
10
+
11
+ class CSV < Spreet::Handler
12
+
13
+ # Read a CSV file and create its Spreet document
14
+ def self.read(file, options={})
15
+ spreet = Spreet::Document.new
16
+ sheet = spreet.sheets.add
17
+ Spreet::CSV.foreach(file) do |row|
18
+ sheet.row *row
19
+ end
20
+ return spreet
21
+ end
22
+
23
+
24
+ # Write a Spreet to a CSV file
25
+ def self.write(spreet, file, options={})
26
+ sheet = spreet.sheets[options[:sheet]||0]
27
+ Spreet::CSV.open(file, "wb") do |csv|
28
+ sheet.each_row do |row|
29
+ csv << row.collect{|c| c.text}
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ class ExcelCSV < Spreet::Handler
37
+
38
+ # Read a CSV file and create its Spreet document
39
+ def self.read(file, options={})
40
+ spreet = Spreet::Document.new
41
+ sheet = spreet.sheets.add
42
+ options = {:col_sep=>';'}.merge(options)
43
+ ic = Iconv.new('utf-8', 'cp1252')
44
+ Spreet::CSV.foreach(file, options) do |row|
45
+ sheet.row *(row.collect{|v| ic.iconv(v.to_s)})
46
+ end
47
+ return spreet
48
+ end
49
+
50
+
51
+ # Write a Spreet to a CSV file
52
+ def self.write(spreet, file, options={})
53
+ sheet = spreet.sheets[options[:sheet]||0]
54
+ options = {:col_sep=>';'}.merge(options)
55
+ ic = Iconv.new('cp1252', 'utf-8')
56
+ Spreet::CSV.open(file, "wb", options) do |csv|
57
+ sheet.each_row do |row|
58
+ csv << row.collect{|c| ic.iconv(c.text)}
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+ require 'zip/zip'
3
+
4
+ module Spreet
5
+ module Handlers
6
+ class OpenDocument < Spreet::Handler
7
+ DATE_REGEXP = /\%./
8
+ DATE_ELEMENTS = {
9
+ "m" => "<number:month number:style=\"long\"/>",
10
+ "d" => "<number:day number:style=\"long\"/>",
11
+ "Y" => "<number:year/>"
12
+ }
13
+
14
+
15
+ def self.mimetype
16
+ end
17
+
18
+ def self.xmlec(string)
19
+ zs = string.to_s.gsub('&', '&amp;').gsub('\'', '&apos;').gsub('<', '&lt;').gsub('>', '&gt;')
20
+ zs.force_encoding('US-ASCII') if zs.respond_to?(:force_encoding)
21
+ return zs
22
+ end
23
+
24
+
25
+ def self.write(spreet, file, options={})
26
+ xml_escape = "to_s.gsub('&', '&amp;').gsub('\\'', '&apos;').gsub('<', '&lt;').gsub('>', '&gt;')"
27
+ xml_escape << ".force_encoding('US-ASCII')" if xml_escape.respond_to?(:force_encoding)
28
+ mimetype = "application/vnd.oasis.opendocument.spreadsheet"
29
+ # name = #{table.model.name}.model_name.human.gsub(/[^a-z0-9]/i,'_')
30
+ Zip::ZipOutputStream.open(file) do |zile|
31
+ # MimeType in first place
32
+ zile.put_next_entry('mimetype', nil, nil, Zip::ZipEntry::STORED)
33
+ zile << mimetype
34
+
35
+ # Manifest
36
+ zile.put_next_entry('META-INF/manifest.xml')
37
+ zile << ("<?xml version=\"1.0\" encoding=\"UTF-8\"?><manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"><manifest:file-entry manifest:media-type=\"#{mimetype}\" manifest:full-path=\"/\"/><manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/></manifest:manifest>")
38
+ zile.put_next_entry('content.xml')
39
+
40
+ zile << ("<?xml version=\"1.0\" encoding=\"UTF-8\"?><office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\" xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\" xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\" xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\" xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\" xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\" xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\" xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\" xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\" xmlns:math=\"http://www.w3.org/1998/Math/MathML\" xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\" xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\" xmlns:ooo=\"http://openoffice.org/2004/office\" xmlns:ooow=\"http://openoffice.org/2004/writer\" xmlns:oooc=\"http://openoffice.org/2004/calc\" xmlns:dom=\"http://www.w3.org/2001/xml-events\" xmlns:xforms=\"http://www.w3.org/2002/xforms\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:field=\"urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0\" office:version=\"1.1\"><office:scripts/>")
41
+ # Styles
42
+ default_date_format = '%d/%m%Y' # ::I18n.translate("date.formats.default")
43
+ zile << ("<office:automatic-styles><style:style style:name=\"co1\" style:family=\"table-column\"><style:table-column-properties fo:break-before=\"auto\" style:use-optimal-column-width=\"true\"/></style:style><style:style style:name=\"header\" style:family=\"table-cell\"><style:text-properties fo:font-weight=\"bold\" style:font-weight-asian=\"bold\" style:font-weight-complex=\"bold\"/></style:style><number:date-style style:name=\"K4D\" number:automatic-order=\"true\"><number:text>"+default_date_format.gsub(DATE_REGEXP){|x| "</number:text>"+DATE_ELEMENTS[x[1..1]]+"<number:text>"} +"</number:text></number:date-style><style:style style:name=\"ce1\" style:family=\"table-cell\" style:data-style-name=\"K4D\"/></office:automatic-styles>")
44
+
45
+ zile << ("<office:body><office:spreadsheet>")
46
+ # Tables
47
+ for sheet in spreet.sheets
48
+ zile << ("<table:table table:name=\"#{xmlec(sheet.name)}\">")
49
+ zile << ("<table:table-column table:number-columns-repeated=\"#{sheet.bound.x+1}\"/>")
50
+ # zile << ("<table:table-header-rows><table:table-row>"+columns_headers(table).collect{|h| "<table:table-cell table:style-name=\"header\" office:value-type=\"string\"><text:p>'+(#{h}).#{xml_escape}+'</text:p></table:table-cell>"}.join+"</table:table-row></table:table-header-rows>")
51
+ sheet.each_row do |row| # #{record} in #{table.records_variable_name}\n"
52
+ zile << "<table:table-row>"
53
+ for cell in row
54
+ zile << "<table:table-cell"+(if cell.type == :decimal
55
+ " office:value-type=\"float\" office:value=\"#{xmlec(cell.value)}\""
56
+ elsif cell.type == :boolean
57
+ " office:value-type=\"boolean\" office:boolean-value=\"#{xmlec(cell.value ? 'true' : 'false')}\""
58
+ elsif cell.type == :date
59
+ " office:value-type=\"date\" table:style-name=\"ce1\" office:date-value=\"#{xmlec(cell.value)}\""
60
+ else
61
+ " office:value-type=\"string\""
62
+ end)+"><text:p>"+xmlec(cell.text)+"</text:p></table:table-cell>"
63
+ end
64
+ zile << "</table:table-row>"
65
+ end
66
+ zile << ("</table:table>")
67
+ end
68
+ zile << ("</office:spreadsheet></office:body></office:document-content>")
69
+ end
70
+ # Zile is finished
71
+ end
72
+
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ Gem::Specification.new do |s|
3
+ s.name = "spreet"
4
+ File.open("VERSION", "rb") do |f|
5
+ s.version = f.read
6
+ end
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["Brice Texier"]
10
+ s.date = "2011-09-19"
11
+ s.summary = "Spr[eadsh]eet handler"
12
+ s.description = "Spr[eadsh]eet handler for CSV(RW), Excel CSV(RW) and ODS(W). The goal is to read and write in many open formats."
13
+ s.email = "brice.texier@ekylibre.org"
14
+ s.extra_rdoc_files = [
15
+ "LICENSE.txt",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = `git ls-files`.split("\n")
19
+ s.homepage = "http://github.com/burisu/spreet"
20
+ s.licenses = ["MIT"]
21
+ s.require_paths = ["lib"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ s.specification_version = 3
25
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency("fastercsv", [">= 0"])
27
+ s.add_runtime_dependency("libxml-ruby", [">= 0"])
28
+ s.add_runtime_dependency("rubyzip", [">= 0.9.4"])
29
+ else
30
+ s.add_dependency("fastercsv", [">= 0"])
31
+ s.add_dependency("libxml-ruby", [">= 0"])
32
+ s.add_dependency("rubyzip", [">= 0.9.4"])
33
+ end
34
+ else
35
+ s.add_dependency("fastercsv", [">= 0"])
36
+ s.add_dependency("libxml-ruby", [">= 0"])
37
+ s.add_dependency("rubyzip", [">= 0.9.4"])
38
+ end
39
+ end
40
+
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ require 'spreet'
15
+
16
+ class Test::Unit::TestCase
17
+ end
@@ -0,0 +1,17 @@
1
+ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
2
+ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
3
+ 1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
4
+ 1,3,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0
5
+ 1,4,6,4,1,0,0,0,0,0,0,0,0,0,0,0,0
6
+ 1,5,10,10,5,1,0,0,0,0,0,0,0,0,0,0,0
7
+ 1,6,15,20,15,6,1,0,0,0,0,0,0,0,0,0,0
8
+ 1,7,21,35,35,21,7,1,0,0,0,0,0,0,0,0,0
9
+ 1,8,28,56,70,56,28,8,1,0,0,0,0,0,0,0,0
10
+ 1,9,36,84,126,126,84,36,9,1,0,0,0,0,0,0,0
11
+ 1,10,45,120,210,252,210,120,45,10,1,0,0,0,0,0,0
12
+ 1,11,55,165,330,462,462,330,165,55,11,1,0,0,0,0,0
13
+ 1,12,66,220,495,792,924,792,495,220,66,12,1,0,0,0,0
14
+ 1,13,78,286,715,1287,1716,1716,1287,715,286,78,13,1,0,0,0
15
+ 1,14,91,364,1001,2002,3003,3432,3003,2002,1001,364,91,14,1,0,0
16
+ 1,15,105,455,1365,3003,5005,6435,6435,5005,3003,1365,455,105,15,1,0
17
+ 1,16,120,560,1820,4368,8008,11440,12870,11440,8008,4368,1820,560,120,16,1
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+ require 'helper'
3
+
4
+ class TestSpreet < Test::Unit::TestCase
5
+
6
+ def test_coordinates
7
+ assert_equal Spreet::Coordinates.new(0,0), Spreet::Coordinates.new("A1")
8
+ assert_equal Spreet::Coordinates.new(0,0), Spreet::Coordinates.new("0-0")
9
+ assert_equal Spreet::Coordinates.new(1,1), Spreet::Coordinates.new("B2")
10
+ assert_equal Spreet::Coordinates.new(2,2), Spreet::Coordinates.new(:x=>2, :y=>2)
11
+ assert_equal Spreet::Coordinates.new(3,3), Spreet::Coordinates.new(3,3)
12
+ assert_equal Spreet::Coordinates.new(3,3), Spreet::Coordinates.new([3,3])
13
+ assert_equal Spreet::Coordinates.new(4,4), Spreet::Coordinates.new(Spreet::Coordinates.new(4,4).to_i)
14
+ assert_equal Spreet::Coordinates.new(5,5), Spreet::Coordinates.new(Spreet::Coordinates.new(5,5))
15
+ assert_equal("D25", Spreet::Coordinates.new(3,24).to_s)
16
+ assert_equal([3, 24], Spreet::Coordinates.new(3,24).to_a)
17
+ assert_equal({:x=>3, :y=>24}, Spreet::Coordinates.new(3,24).to_hash)
18
+ assert Spreet::Coordinates.new(0,0) <=> Spreet::Coordinates.new(0,1)
19
+ assert Spreet::Coordinates.new(0,1) <=> Spreet::Coordinates.new(1,0)
20
+ end
21
+
22
+ def test_spreet_version
23
+ assert_not_nil Spreet::VERSION
24
+ assert_not_nil Spreet::VERSION::MAJOR
25
+ assert_not_nil Spreet::VERSION::MINOR
26
+ assert_not_nil Spreet::VERSION::TINY
27
+ assert_not_nil Spreet::VERSION::PATCH
28
+ assert_equal(Spreet::VERSION::TINY, Spreet::VERSION::PATCH, "PATCH code must have the same value as TINY")
29
+ assert((Spreet::VERSION::MAJOR > 0 or Spreet::VERSION::MINOR > 0 or Spreet::VERSION::TINY > 0), "Version cannot be 0.0.0")
30
+ end
31
+
32
+ def test_spreet
33
+ assert_not_nil Spreet
34
+
35
+ spreet = Spreet::Document.new
36
+ assert_not_nil spreet
37
+ sheet = spreet.sheets.add
38
+ assert_not_nil sheet
39
+ assert_not_nil spreet.sheets.add("Feuille 2")
40
+ assert_not_nil spreet.sheets.add("ソフト 3")
41
+
42
+ assert_equal Spreet::Sheet, spreet.sheets[1].class
43
+ assert_equal "Feuille 2", spreet.sheets[1].name
44
+
45
+ assert_equal Spreet::Sheet, spreet.sheets["ソフト 3"].class
46
+ assert_equal "ソフト 3", spreet.sheets["ソフト 3"].name
47
+
48
+ assert_not_nil sheet[0,0]
49
+ sheet[0,0] = "Cell A1"
50
+ spreet.sheets[1][0] = "Cellule A1"
51
+ spreet.sheets["ソフト 3"]["A1"] = "セル A1"
52
+
53
+ assert_not_nil sheet["C30"]
54
+
55
+ sheet["F20"] = Date.today
56
+
57
+ spreet.write("test/samples/cleaned-nothing.ods")
58
+ end
59
+
60
+
61
+ def test_handlers
62
+ doc = nil
63
+ assert_nothing_raised do
64
+ doc = Spreet::Document.read("test/samples/pascal.csv")
65
+ end
66
+
67
+ sheet = doc.sheets[0]
68
+ sheet.each_row do |row|
69
+ for cell in row
70
+ if cell.text.to_i == 0
71
+ cell.clear!
72
+ end
73
+ end
74
+ end
75
+
76
+ doc.write("test/samples/cleaned-pascal.csv", :format=>:xcsv)
77
+ doc.write("test/samples/cleaned-pascal.ods")
78
+
79
+ assert_nothing_raised do
80
+ doc = Spreet::Document.read("test/samples/cleaned-pascal.csv", :format=>:xcsv)
81
+ end
82
+
83
+ FileUtils.rm_f("test/samples/cleaned-pascal.csv")
84
+ end
85
+
86
+
87
+
88
+
89
+ end
90
+
91
+
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreet
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brice Texier
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-19 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fastercsv
16
+ requirement: &11182000 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *11182000
25
+ - !ruby/object:Gem::Dependency
26
+ name: libxml-ruby
27
+ requirement: &11180960 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *11180960
36
+ - !ruby/object:Gem::Dependency
37
+ name: rubyzip
38
+ requirement: &11180240 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.4
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *11180240
47
+ description: Spr[eadsh]eet handler for CSV(RW), Excel CSV(RW) and ODS(W). The goal
48
+ is to read and write in many open formats.
49
+ email: brice.texier@ekylibre.org
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files:
53
+ - LICENSE.txt
54
+ - README.rdoc
55
+ files:
56
+ - .gitignore
57
+ - Gemfile
58
+ - LICENSE.txt
59
+ - README.rdoc
60
+ - Rakefile
61
+ - VERSION
62
+ - lib/spreet.rb
63
+ - lib/spreet/handlers.rb
64
+ - lib/spreet/handlers/csv.rb
65
+ - lib/spreet/handlers/open_document.rb
66
+ - spreet.gemspec
67
+ - test/helper.rb
68
+ - test/samples/pascal.csv
69
+ - test/test_spreet.rb
70
+ homepage: http://github.com/burisu/spreet
71
+ licenses:
72
+ - MIT
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 1.8.11
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: Spr[eadsh]eet handler
95
+ test_files: []