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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a2d8d287bb71584ad802cd18d4414c4961188dbb
4
+ data.tar.gz: 2fb3326dbdd89711ae5f3e3dea251c117d7b4016
5
+ SHA512:
6
+ metadata.gz: 088059597e1261b79a0f94fef327042b48c83819c2348f809aab35a62303b2a9f8b7629f25c30759425c5e471e7327259c6f292cdb58590ae7555c3b80d2a2a1
7
+ data.tar.gz: 304a17f577bb54a2292995b7504f44956584bd55b8bab7608127ab4fa14571331dd6ea61cf3c588c7e8185c9f30d7b2721f3604a76464c87704a490b79c45054
data/README.rdoc CHANGED
@@ -1,7 +1,11 @@
1
1
  = Spreet
2
+ {<img src="https://badge.fury.io/rb/spreet.png" alt="Gem Version" />}[http://badge.fury.io/rb/spreet]
3
+ {<img src="https://secure.travis-ci.org/ekylibre/spreet.png"/>}[http://travis-ci.org/ekylibre/spreet]
2
4
 
3
5
  Universal handler for spr[eadsh]eets.
4
6
 
7
+ Compatible with Ruby ≥ 1.9.2.
8
+
5
9
  == Why ?
6
10
  This gems is a handler for spreadsheets. With its independent API, it is possible to create, update files in some formats. Today the list is not very long:
7
11
 
@@ -24,7 +28,7 @@ This gems is a handler for spreadsheets. With its independent API, it is possibl
24
28
  # ...or more classic style...
25
29
  sheet[1,0] = "First name"
26
30
  # ...or if necessary as a Hash
27
- sheet[:x=>2, :y=>0] = "Born on"
31
+ sheet[x: 2, y: 0] = "Born on"
28
32
 
29
33
  sheet.next_row
30
34
  for person in People.all
@@ -37,13 +41,13 @@ This gems is a handler for spreadsheets. With its independent API, it is possibl
37
41
  sheet.write("people-2.csv", :format=>:xcsv) # CSV for Excel
38
42
  # or write it as an Open Document Spreadsheet
39
43
  sheet.write("people-3.ods")
44
+
45
+ # Read an existing document
46
+ doc = Spreet::Document.read("doc.ods")
40
47
 
41
- == To do soon
48
+ == To do
42
49
 
43
50
  * Add style management for cells
44
51
  * Add Header/Footer
45
52
  * HTML Writer
46
- * PDF Writer like OpenOffice/LibreOffice would make it
47
-
48
- == Travis
49
- {<img src="https://secure.travis-ci.org/burisu/spreet.png"/>}[http://travis-ci.org/burisu/spreet]
53
+ * PDF Writer like LibreOffice would make it
data/lib/spreet.rb CHANGED
@@ -1,341 +1,17 @@
1
- # encoding: utf-8
2
1
  require 'pathname'
3
- require 'duration'
4
- require 'money'
5
- require 'time'
6
- require 'big_array'
7
- require 'spreet/coordinates'
8
-
9
- # Create class for arrays
10
- BigArray.new("Cells", 10, 3)
11
2
 
12
3
  module Spreet
13
-
14
- module VERSION
15
- version = nil
16
- File.open(File.join(File.dirname(__FILE__), "..", "VERSION")) {|f| version = f.read.split('.')}
17
- MAJOR = version[0].to_i.freeze
18
- MINOR = version[1].to_i.freeze
19
- TINY = version[2].to_i.freeze
20
- PATCH = TINY.freeze
21
- PRE = version[3].freeze
22
- STRING = version.freeze
23
- end
24
-
25
-
26
- # Represents a cell in a sheet
27
- class Cell
28
- attr_reader :text, :value, :type, :sheet, :coordinates
29
- attr_accessor :annotation
30
-
31
- def initialize(sheet, *args)
32
- @sheet = sheet
33
- @coordinates = Coordinates.new(*args)
34
- self.value = nil
35
- @empty = true
36
- @covered = false # determine_covered
37
- @annotation = nil
38
- end
39
-
40
- def value=(val)
41
- if val.is_a?(Cell)
42
- @value = val.value
43
- @type = val.type
44
- self.text = val.text
45
- @empty = val.empty?
46
- @annotation = val.annotation
47
- else
48
- @value = val
49
- @type = determine_type
50
- self.text = val
51
- @empty = false
52
- end
53
- end
54
-
55
- def empty?
56
- @empty
57
- end
58
-
59
- def covered?
60
- @covered
61
- end
62
-
63
- def clear!
64
- self.value = nil
65
- @empty = true
66
- end
67
-
68
- def remove!
69
- @sheet.remove(self.coordinates)
70
- end
71
-
72
- def <=>(other_cell)
73
- self.coordinates <=> other_cell.coordinates
74
- end
75
-
76
- def text=(val)
77
- @text = val.to_s
78
- end
79
-
80
- def inspect
81
- "<#{self.coordinates}: #{self.text.inspect}#{'('+self.value.inspect+')' if self.text != self.value}>"
82
- end
83
-
84
- private
85
-
86
-
87
- def determine_type
88
- if value.is_a? Date or value.is_a? DateTime
89
- :date
90
- elsif value.is_a? Numeric # or percentage
91
- :float
92
- elsif value.is_a? Money
93
- :currency
94
- elsif value.is_a? Duration
95
- :time
96
- elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
97
- :boolean
98
- else # if value.is_a?(String)
99
- :string
100
- end
101
- end
102
-
103
- end
104
-
105
- class Sheet
106
- attr_reader :document, :name, :columns
107
- attr_accessor :current_row
108
-
109
- def initialize(document, name=nil)
110
- @document = document
111
- self.name = name
112
- raise ArgumentError.new("Must be a Document") unless document.is_a? Document
113
- @current_row = 0
114
- @cells = {} # BigArray::Cells.new
115
- @bound = compute_bound
116
- end
117
-
118
- def name=(value)
119
- unless value
120
- value = (@document.sheets.count > 0 ? @document.sheets[-1].name.succ : "Sheet 1")
121
- end
122
- raise ArgumentError.new("Name of sheet must be given") if value.to_s.strip.size.zero?
123
- if @document.sheets[value]
124
- raise ArgumentError.new("Name of sheet must be unique")
125
- end
126
- @name = value
127
- end
128
-
129
- def next_row(increment = 1)
130
- @current_row += increment
131
- end
132
-
133
- def previous_row(increment = 1)
134
- @current_row -= increment
135
- end
136
-
137
- def [](*args)
138
- coord = Coordinates.new(*args)
139
- @cells[coord.to_i] ||= Cell.new(self, coord)
140
- return @cells[coord.to_i]
141
- end
142
-
143
- def []=(*args)
144
- value = args.delete_at(-1)
145
- cell = self[*args]
146
- cell.value = value
147
- @updated = true
148
- end
149
-
150
- def row(*args)
151
- options = {}
152
- options = args.delete_at(-1) if args[-1].is_a? Hash
153
- row = options[:row] || @current_row
154
- args.each_index do |index|
155
- self[index, row] = args[index]
156
- end
157
- next_row
158
- end
159
-
160
- def rows(index)
161
- row = []
162
- for i in 0..bound.x
163
- row[i] = self[i, index]
164
- end
165
- return row
166
- end
167
-
168
- def each_row(&block)
169
- for j in 0..bound.y
170
- yield rows(j)
171
- end
172
- end
173
-
174
- # Find or build cell
175
- def cell(*args)
176
- return c
177
- end
178
-
179
- def bound
180
- if @updated
181
- compute_bound
182
- else
183
- @bound
184
- end
185
- end
186
-
187
- def remove!(coordinates)
188
- raise ArgumentError.new("Must be a Coordinates") unless document.is_a?(Coordinates)
189
- @cells.delete(coordinates.to_i)
190
- @updated = true
191
- end
192
-
193
- # Moves the sheet to an other position in the list of sheets
194
- def move_to(position)
195
- @document.sheets.move_at(self, position)
196
- end
197
-
198
- # Moves the sheet higher in the list of sheets
199
- def move_higher(increment=1)
200
- @document.sheets.move(self, increment)
201
- end
202
-
203
- # Moves the sheet lower in the list of sheets
204
- def move_lower(increment=1)
205
- @document.sheets.move(self, -increment)
206
- end
207
-
208
- private
209
-
210
- def compute_bound
211
- bound = Coordinates.new(0,0)
212
- for index, cell in @cells
213
- # for cell in @cells.compact
214
- unless cell.empty?
215
- bound.x = cell.coordinates.x if cell.coordinates.x > bound.x
216
- bound.y = cell.coordinates.y if cell.coordinates.y > bound.y
217
- end
218
- end
219
- @updated = false
220
- @bound = bound
221
- return @bound
222
- end
223
-
224
- end
225
-
226
-
227
- class Sheets
228
-
229
- def initialize(document)
230
- raise ArgumentError.new("Must be a Document") unless document.is_a?(Document)
231
- @document = document
232
- @array = []
233
- end
234
-
235
- def count
236
- @array.size
237
- end
238
-
239
- def index(name_or_sheet)
240
- if name_or_sheet.is_a? String
241
- @array.each_index do |i|
242
- return i if @array[i].name == name_or_sheet
243
- end
244
- elsif name_or_sheet.is_a? Integer
245
- return (@array[name_or_sheet].nil? ? nil : name_or_sheet)
246
- else
247
- return @array.index(name_or_sheet)
248
- end
249
- end
250
-
251
- def add(name=nil, position=-1)
252
- sheet = Sheet.new(@document, name)
253
- @array.insert(position, sheet)
254
- return sheet
255
- end
256
-
257
- def [](sheet)
258
- sheet = index(sheet)
259
- return (sheet.is_a?(Integer) ? @array[sheet] : nil)
260
- end
261
-
262
- def remove(sheet)
263
- @array.delete_at(index(sheet))
264
- end
265
-
266
- def move(sheet, shift=0)
267
- position = index(sheet) + shift
268
- position = 0 if position < 0
269
- position = self.count-1 if position >= self.count
270
- move_at(sheet, position)
271
- end
272
-
273
- def move_at(sheet, position=-1)
274
- if i = index(sheet)
275
- @array.insert(position, @array.delete_at(i))
276
- end
277
- end
278
-
279
- def each(&block)
280
- for item in @array
281
- yield item
282
- end
283
- end
284
-
285
- end
286
-
287
-
288
- class Document
289
- attr_reader :sheets
290
- @@handlers = {}
291
- @@associations = {}
292
-
293
- def initialize(option={})
294
- @sheets = Sheets.new(self)
295
- end
296
-
297
- def write(file, options={})
298
- handler = self.class.extract_handler(file, options.delete(:format))
299
- handler.write(self, file, options)
300
- end
301
-
302
- class << self
303
-
304
- def register_handler(klass, name, options={})
305
- if klass.respond_to?(:read) or klass.respond_to?(:write)
306
- if name.is_a?(Symbol)
307
- @@handlers[name] = klass # options.merge(:class=>klass)
308
- elsif
309
- raise ArgumentError.new("Name is invalid. Symbol expected, #{name.class.name} got.")
310
- end
311
- else
312
- raise ArgumentError.new("Handler do not support :read or :write method.")
313
- end
314
- end
315
-
316
- def read(file, options={})
317
- handler = extract_handler(file, options.delete(:format))
318
- return handler.read(file, options)
319
- end
320
-
321
- def extract_handler(file, handler_name=nil)
322
- file_path = Pathname.new(file)
323
- extension = file_path.extname.to_s[1..-1]
324
- if !handler_name and extension.size > 0
325
- handler_name = extension.to_sym
326
- end
327
- if @@handlers[handler_name]
328
- return @@handlers[handler_name]
329
- else
330
- raise ArgumentError.new("No corresponding handler (#{handler_name.inspect}). Available: #{@@handlers.keys.collect{|k| k.inspect}.join(', ')}.")
331
- end
332
- end
333
-
334
- end
335
-
336
-
337
- end
338
-
4
+ autoload :VERSION, 'spreet/version'
5
+ autoload :Coordinates, 'spreet/coordinates'
6
+ autoload :Cell, 'spreet/cell'
7
+ autoload :Sheet, 'spreet/sheet'
8
+ autoload :Sheets, 'spreet/sheets'
9
+ autoload :Document, 'spreet/document'
10
+ autoload :Handlers, 'spreet/handlers'
339
11
  end
340
12
 
341
- require 'spreet/handlers'
13
+ Spreet::Document.register_handler Spreet::Handlers::CSV, :csv
14
+ Spreet::Document.register_handler Spreet::Handlers::ExcelCSV, :xcsv
15
+ # Spreet::Document.register_handler Spreet::Handlers::HTML, :html
16
+ Spreet::Document.register_handler Spreet::Handlers::OpenDocument, :ods
17
+ # Spreet::Document.register_handler Spreet::Handlers::PDF, :pdf
@@ -0,0 +1,87 @@
1
+ require 'money'
2
+ require 'time'
3
+ require 'duration'
4
+
5
+ module Spreet
6
+
7
+ # Represents a cell in a sheet
8
+ class Cell
9
+ attr_reader :text, :value, :type, :sheet, :coordinates
10
+ attr_accessor :annotation
11
+
12
+ def initialize(sheet, *args)
13
+ @sheet = sheet
14
+ @coordinates = Coordinates.new(*args)
15
+ self.value = nil
16
+ @empty = true
17
+ @covered = false # determine_covered
18
+ @annotation = nil
19
+ end
20
+
21
+ def value=(val)
22
+ if val.is_a?(Cell)
23
+ @value = val.value
24
+ @type = val.type
25
+ self.text = val.text
26
+ @empty = val.empty?
27
+ @annotation = val.annotation
28
+ else
29
+ @value = val
30
+ @type = determine_type
31
+ self.text = val
32
+ @empty = false
33
+ end
34
+ end
35
+
36
+ def empty?
37
+ @empty
38
+ end
39
+
40
+ def covered?
41
+ @covered
42
+ end
43
+
44
+ def clear!
45
+ self.value = nil
46
+ @empty = true
47
+ end
48
+
49
+ def remove!
50
+ @sheet.remove(self.coordinates)
51
+ end
52
+
53
+ def <=>(other_cell)
54
+ self.coordinates <=> other_cell.coordinates
55
+ end
56
+
57
+ def text=(val)
58
+ @text = val.to_s
59
+ end
60
+
61
+ def inspect
62
+ "<#{self.coordinates}: #{self.text.inspect}#{'('+self.value.inspect+')' if self.text != self.value}>"
63
+ end
64
+
65
+ private
66
+
67
+
68
+ def determine_type
69
+ if value.is_a? Date or value.is_a? DateTime
70
+ :date
71
+ elsif value.is_a? Numeric # or percentage
72
+ :float
73
+ elsif value.is_a? Money
74
+ :currency
75
+ elsif value.is_a? Duration
76
+ :time
77
+ elsif value.is_a?(TrueClass) or value.is_a?(FalseClass)
78
+ :boolean
79
+ else # if value.is_a?(String)
80
+ :string
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+
87
+ end