spreet 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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