ActiveExcel 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/Manifest.txt +7 -0
- data/README.txt +48 -0
- data/Rakefile +30 -0
- data/bin/active_excel +3 -0
- data/lib/active_excel.rb +432 -0
- data/test/test_active_excel.rb +180 -0
- metadata +73 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= active_excel
|
2
|
+
|
3
|
+
* FIX (url)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
FIX (describe your package)
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* FIX (list of features or problems)
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
FIX (code sample of usage)
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
* FIX (list of requirements)
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
* FIX (sudo gem install, anything else)
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2009 FIX
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
$:.unshift(File.dirname(__FILE__) + "/lib")
|
6
|
+
require 'active_excel'
|
7
|
+
|
8
|
+
Hoe.spec 'active_excel' do |p|
|
9
|
+
#self.rubyforge_name = 'active_excelx' # if different than 'active_excel'
|
10
|
+
#p.developer('FIX', 'FIX@example.com')
|
11
|
+
#Hoe.new('ActiveExcel', ActiveExcel::VERSION) do |p|
|
12
|
+
p.name = "ActiveExcel"
|
13
|
+
p.author = "Rob Whitener"
|
14
|
+
p.description = "Excel as the container of model objects"
|
15
|
+
p.email = 'robwhitener@gmail.com'
|
16
|
+
p.summary = <<EXCEL
|
17
|
+
Use Excel to contain model objects. Each row in a sheet represents an object, each sheet in a workbook
|
18
|
+
represents a class. Make sure that you rename your sheets - this gem uses ActiveSupport to take advantage
|
19
|
+
of the pluralization familiar to Rails developers. Modeled liberally on ActiveRecord, not quite as cool but
|
20
|
+
hopefully useful enough. Cheers.
|
21
|
+
EXCEL
|
22
|
+
p.url = "http://robsexocortex.blogspot.com/2009/06/activeexcel.html"
|
23
|
+
p.clean_globs = ['test/actual'] # Remove this directory on "rake clean"
|
24
|
+
p.remote_rdoc_dir = '' # Release to root
|
25
|
+
#p.changes = p.paragraphs_of('CHANGELOG', 0..1).join("\n\n")
|
26
|
+
# * extra_deps - An array of rubygem dependencies.
|
27
|
+
#end
|
28
|
+
end
|
29
|
+
|
30
|
+
# vim: syntax=ruby
|
data/bin/active_excel
ADDED
data/lib/active_excel.rb
ADDED
@@ -0,0 +1,432 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
require 'activesupport'
|
3
|
+
|
4
|
+
class ExcelConstants
|
5
|
+
XLBLACK = 1
|
6
|
+
XLRED = 3
|
7
|
+
XLGREEN = 4
|
8
|
+
XLBLUE = 5
|
9
|
+
XLYELLOW = 6
|
10
|
+
XLMAGENTA = 7
|
11
|
+
XLCYAN = 8
|
12
|
+
XLBROWN = 9
|
13
|
+
XLOLIVE = 10
|
14
|
+
XLROYAL = 11
|
15
|
+
XLVOMIT = 12
|
16
|
+
XLPURPLE = 13
|
17
|
+
XLNOCOLOR = -4142
|
18
|
+
end
|
19
|
+
|
20
|
+
module ActiveExcel
|
21
|
+
VERSION = '0.0.1'
|
22
|
+
#This class conatins the main Excel functionality that will be inherited
|
23
|
+
#by objects which intend to wrap data in the spreadsheet
|
24
|
+
class Root
|
25
|
+
attr_accessor :workbook, :workbook_name, :id, :fields, :dirty, :is_new, :this_workbook
|
26
|
+
@@excel = nil
|
27
|
+
@@sheets = nil
|
28
|
+
@@workbook = nil
|
29
|
+
@@sheet_name = nil
|
30
|
+
|
31
|
+
def initialize()
|
32
|
+
#this will be called by the new method and will be called for
|
33
|
+
#each instance of any subclass that is created.
|
34
|
+
#The initialize routine will handel adding attributes to the
|
35
|
+
#new instance based on the fieldnames from the spreadsheet.
|
36
|
+
#If Find was used to get an object out of the sheet, then the
|
37
|
+
#attributes will be populated. Each instance will also have
|
38
|
+
#an id field that corresponds to its row number in the spreadsheet
|
39
|
+
@fields = nil
|
40
|
+
@this_workbook = @@workbook
|
41
|
+
add_attributes
|
42
|
+
@id = -1 #the id can have a default value of -1, since there is no -1 row in a sheet
|
43
|
+
@dirty = false
|
44
|
+
@is_new = true
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self ###Class methods
|
49
|
+
|
50
|
+
#Config is a hash that expects the following parameters
|
51
|
+
# :wkbook -> The name of the workbook to open
|
52
|
+
# :path -> pathe where the workbook can be found if the workbook isn't in the my docs folder
|
53
|
+
def workbook_connect(config=nil)
|
54
|
+
@workbook_name = config[:wkbook]
|
55
|
+
if config[:path]
|
56
|
+
#there was a path specified, prepend that to the workbook name
|
57
|
+
@workbook_name = config[:path] + "/" + @workbook_name
|
58
|
+
puts "Fully Qualified Workbook Path:>> #{@workbook_name}"
|
59
|
+
else
|
60
|
+
#otherwise, figure out where MyDocuments is
|
61
|
+
@workbook_name = ENV['USERPROFILE'] + "\\" + "My Documents" + "\\" + @workbook_name
|
62
|
+
end
|
63
|
+
#create the Excel application
|
64
|
+
|
65
|
+
begin
|
66
|
+
@@excel = WIN32OLE::connect('Excel.Application') if !@@excel
|
67
|
+
rescue
|
68
|
+
@@excel = WIN32OLE::new('Excel.Application')
|
69
|
+
end
|
70
|
+
|
71
|
+
#we now have a connection to a workbook.
|
72
|
+
#first, check to see if the desired workbook is open, if it is put that in @@workbook
|
73
|
+
if @@excel.Workbooks.Count > 0 then
|
74
|
+
#there are workbooks open, grab the one we want by name
|
75
|
+
@@workbook = nil
|
76
|
+
for wkbk in @@excel.Workbooks
|
77
|
+
if wkbk.Name == config[:wkbook] then
|
78
|
+
@@workbook = wkbk
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
#at this point if @@workbook is nil, then go ahead and open it.
|
85
|
+
if @@workbook == nil then
|
86
|
+
@@workbook = @@excel.Workbooks.Open(@workbook_name)
|
87
|
+
end
|
88
|
+
@@excel.Visible = 1
|
89
|
+
#get all the sheet names
|
90
|
+
@@sheets ||= {}
|
91
|
+
i = 1
|
92
|
+
for worksheet in @@workbook.Worksheets
|
93
|
+
@@sheets[i] = worksheet.Name
|
94
|
+
i += 1
|
95
|
+
end
|
96
|
+
@@workbook
|
97
|
+
end
|
98
|
+
|
99
|
+
def sheet_names
|
100
|
+
@@sheets
|
101
|
+
end
|
102
|
+
|
103
|
+
def load_constants
|
104
|
+
begin
|
105
|
+
ExcelConstants::XLDown #try to access a constant,
|
106
|
+
rescue
|
107
|
+
#if an error is thrown, load them.
|
108
|
+
WIN32OLE.const_load(@@excel, ExcelConstants) #load Excel constants into the module.
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def connected?
|
113
|
+
return @@excel ? true : false
|
114
|
+
end
|
115
|
+
|
116
|
+
def workbook_disconnect
|
117
|
+
#this method will save any changes to the workbook and then close it down.
|
118
|
+
@@workbook.Save if @@workbook
|
119
|
+
@@workbook.Close if @@workbook
|
120
|
+
@@workbook = nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def excel_disconnect
|
124
|
+
@@excel.Quit if @@excel
|
125
|
+
@@excel = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
#Define a sheet name to use, in case the naming convention doesn't work for
|
129
|
+
#particular situations
|
130
|
+
def set_sheet_name(sht)
|
131
|
+
#Add the sheetname as a class variable
|
132
|
+
class_eval{ class_variable_set(:"@@sheet_name",sht.to_s)}
|
133
|
+
end
|
134
|
+
|
135
|
+
#This method will find one or more records in the spreadsheet and
|
136
|
+
#return them as ActiveExcel objects.
|
137
|
+
def find(rec_no)
|
138
|
+
|
139
|
+
if rec_no.is_a? Integer
|
140
|
+
#if its just an integer, then look up by id
|
141
|
+
return find_by_id(rec_no)
|
142
|
+
elsif rec_no.is_a? Hash
|
143
|
+
return nil
|
144
|
+
elsif rec_no.is_a? Symbol
|
145
|
+
case rec_no
|
146
|
+
when :all then return find_all
|
147
|
+
when :first then find_first
|
148
|
+
when :last then find_last
|
149
|
+
end
|
150
|
+
end
|
151
|
+
set_sheet_name(self.class.to_s.tableize)
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_by_id(i)
|
156
|
+
return nil if i < 2 #The rowid has to be 2 or greater
|
157
|
+
o = new #create a new object, this will also set the workseet
|
158
|
+
o.this_workbook = @@workbook.clone
|
159
|
+
@wksheet = get_the_worksheet
|
160
|
+
|
161
|
+
#iterate over the list of field names
|
162
|
+
@columns.each_pair do |k,v|
|
163
|
+
#Access the Cell by passing in the Letter cooresponding to the field name and the
|
164
|
+
#Row index (1 based)
|
165
|
+
value = @wksheet.Range("#{v}#{i}").Value
|
166
|
+
o.send(:"#{k.to_s}=",value)
|
167
|
+
|
168
|
+
end
|
169
|
+
o.dirty = false
|
170
|
+
o.is_new = false
|
171
|
+
o.id=i
|
172
|
+
o #return the new object
|
173
|
+
end
|
174
|
+
|
175
|
+
def find_all
|
176
|
+
#basically, grab all the records except the header, roll them into ActiveExcel objects
|
177
|
+
obj_array = Array.new #make a new array to hold the objects
|
178
|
+
#figure out the current region size
|
179
|
+
current_region = get_the_current_region
|
180
|
+
2.upto(current_region.Rows.Count) do |i|
|
181
|
+
o = new
|
182
|
+
o.this_workbook = @@workbook.clone
|
183
|
+
1.upto(current_region.Columns.Count) do |j|
|
184
|
+
field = get_the_field_name_for_index(j-1) #the letters array is 0 based, but excel is 1 based.
|
185
|
+
o.send(:"#{field}=",@wksheet.Cells(i,j).Value.to_s)
|
186
|
+
o.is_new = false
|
187
|
+
o.dirty = false
|
188
|
+
o.id = i
|
189
|
+
end
|
190
|
+
obj_array << o
|
191
|
+
end
|
192
|
+
obj_array
|
193
|
+
end
|
194
|
+
|
195
|
+
#Returns a list of column names. If the columns hash hasn't been created, it will create the
|
196
|
+
#columns hash by scanning the first row of the CurrentRegion (Which is defined as a region on the
|
197
|
+
#active worksheet bounded by blank cells).
|
198
|
+
def column_names(sheet = nil)
|
199
|
+
if !@columns
|
200
|
+
if sheet
|
201
|
+
#if a sheet name was passed in we can just use that to get the column names
|
202
|
+
@wksheet = @@workbook.Worksheets(sheet)
|
203
|
+
else
|
204
|
+
#Test to see if a sheet_name class variable has been set
|
205
|
+
#if it has, then use that value other wise use the class name
|
206
|
+
classvar = self.class_eval{class_variable_get(:"@@sheet_name")}
|
207
|
+
classname = classvar ? classvar : self.class.to_s.tableize
|
208
|
+
puts "Tableized: #{classname}"
|
209
|
+
@wksheet = @@workbook.Worksheets(classname)
|
210
|
+
end
|
211
|
+
#now, grab the collumn names and put them into a hash name=>col_letter
|
212
|
+
row = @wksheet.Cells.CurrentRegion.Rows(1)
|
213
|
+
@columns = {}
|
214
|
+
@letters = []
|
215
|
+
col_name = "A"
|
216
|
+
row.Cells.each do |c|
|
217
|
+
#c represents a Cell in the worksheet
|
218
|
+
@columns[c.Value.to_sym] = col_name.clone
|
219
|
+
@letters << col_name.clone
|
220
|
+
col_name.succ! #advance the col_name one letter
|
221
|
+
end
|
222
|
+
end
|
223
|
+
#return a list of the keys
|
224
|
+
keys = []
|
225
|
+
@columns.each_key {|k| keys << k}
|
226
|
+
keys
|
227
|
+
end
|
228
|
+
|
229
|
+
def columns
|
230
|
+
return @columns
|
231
|
+
end
|
232
|
+
|
233
|
+
def wksheet
|
234
|
+
return @wksheet
|
235
|
+
end
|
236
|
+
|
237
|
+
#This implementation of method_missing will provide methods that will allow records
|
238
|
+
#to be looked up using find_by_<field_name>(value). For simplicity sake, this first
|
239
|
+
#iteration will only allow the search of individual fields.
|
240
|
+
def method_missing(methodid,*args,&blk)
|
241
|
+
#make sure the columns hash has been built
|
242
|
+
puts("METHOD ID --> #{methodid.to_s}")
|
243
|
+
column_names if !@columns
|
244
|
+
|
245
|
+
#if the words 'find_ and 'by' are not part of method id, then get out.
|
246
|
+
return if methodid.to_s !~ /find_.*by/
|
247
|
+
|
248
|
+
field = methodid.to_s.split("by_").last
|
249
|
+
letter = @columns[field.to_sym] #grab the column letter
|
250
|
+
col_index = @letters.index(letter) + 1 #get the column index, add 1 because Excel is one based.
|
251
|
+
|
252
|
+
if methodid.to_s.index("find_all_by")
|
253
|
+
return find_all_by_attribute(field,col_index,args[0])
|
254
|
+
else
|
255
|
+
return find_by_attribute(field,col_index,args[0])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
#this method is caled from within method missing to return all objects
|
260
|
+
#that can be found which contain a particular value for a particular field
|
261
|
+
def find_all_by_attribute(field,col_index,args)
|
262
|
+
lookup_records(field,col_index,args,false)
|
263
|
+
end
|
264
|
+
|
265
|
+
#This method is called by method_missing to return the first object in the sheet which
|
266
|
+
#contains a particular value for a particular field.
|
267
|
+
def find_by_attribute(field,col_index,args)
|
268
|
+
lookup_records(field,col_index,args)
|
269
|
+
end
|
270
|
+
|
271
|
+
def lookup_records(field,col_index,args,first=true)
|
272
|
+
i = 1 #keep track of the row number with this variable
|
273
|
+
@wksheet = get_the_worksheet
|
274
|
+
current_region = get_the_current_region
|
275
|
+
records = Array.new
|
276
|
+
current_region.each do |range|
|
277
|
+
if @wksheet.Cells(i,col_index).Value == args then
|
278
|
+
#the value of the cell in the range matches the value passed into the
|
279
|
+
#missing method. Create a new object and return it
|
280
|
+
o = new
|
281
|
+
o.this_workbook = @@workbook.clone
|
282
|
+
1.upto(current_region.Columns.Count) do |j|
|
283
|
+
#get the column letter
|
284
|
+
l = @letters[j-1]
|
285
|
+
#from the hash, go backward from the column letter to the field name
|
286
|
+
field = @columns.index(l)
|
287
|
+
o.send(:"#{field.to_s}=",@wksheet.Cells(i,j).Value.to_s)
|
288
|
+
end
|
289
|
+
o.id = i
|
290
|
+
o.dirty = false
|
291
|
+
o.is_new = false
|
292
|
+
return o if first
|
293
|
+
records << o
|
294
|
+
break if i == current_region.Rows.Count
|
295
|
+
end
|
296
|
+
i+=1
|
297
|
+
end
|
298
|
+
records
|
299
|
+
end
|
300
|
+
|
301
|
+
#Get the CurrentRegion of the spreadsheet as a standard range
|
302
|
+
def get_the_current_region
|
303
|
+
@wksheet = get_the_worksheet
|
304
|
+
curr_reg_addr = @wksheet.Cells.CurrentRegion.address.to_s.gsub(/\$/,'') #remove $ from address
|
305
|
+
current_region = @wksheet.Range(curr_reg_addr)
|
306
|
+
end
|
307
|
+
|
308
|
+
#returns the field name for a given column letter
|
309
|
+
def get_the_field_name_for_index(index)
|
310
|
+
l = @letters[index]
|
311
|
+
@columns.index(l)
|
312
|
+
end
|
313
|
+
|
314
|
+
def get_the_worksheet
|
315
|
+
puts("-----------------------------")
|
316
|
+
puts("\tself.name.tableize: #{self.name.tableize}")
|
317
|
+
puts("\tsheet_name: #{@@sheet_name}")
|
318
|
+
@@sheet_name = self.name.tableize if !@@sheet_name
|
319
|
+
puts("\tThe SheetName being used is: #{@@sheet_name}\n")
|
320
|
+
puts("-----------------------------")
|
321
|
+
@@workbook.Worksheets(@@sheet_name)
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
end ###End class methods####
|
326
|
+
|
327
|
+
#allows the workbook that an individual object comes from to be stored in the object.
|
328
|
+
#def this_workbook=(wkbk)
|
329
|
+
# @this_workbook = wkbk
|
330
|
+
#end
|
331
|
+
#
|
332
|
+
#def this_workbook
|
333
|
+
# @this_workbook
|
334
|
+
#end
|
335
|
+
|
336
|
+
#Define the attributes of the instance based on what is in the
|
337
|
+
#spreadheet
|
338
|
+
def add_attributes
|
339
|
+
#we should be able to use the column_names here
|
340
|
+
if !@@sheet_name then
|
341
|
+
@fields = self.class.column_names(self.class.to_s.tableize)
|
342
|
+
else
|
343
|
+
@fields = self.class.column_names()
|
344
|
+
end
|
345
|
+
#add a getter/setter pair for each column name
|
346
|
+
@fields.each do |c|
|
347
|
+
#add the setter method
|
348
|
+
self.class_eval do
|
349
|
+
define_method(:"#{c.to_s}=") do |v|
|
350
|
+
instance_variable_set(:"@#{c.to_s}",v)
|
351
|
+
instance_variable_set(:"@dirty",true)
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
#add the getter method
|
356
|
+
self.class_eval do
|
357
|
+
define_method(c) do
|
358
|
+
instance_variable_get(:"@#{c.to_s}")
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
|
365
|
+
#This method will save the object back into the spreadsheet. THIS DOES NOT SAVE
|
366
|
+
#CHANGES TO THE ACTUAL SPREADSHEET THAT IS IN MEMORY.
|
367
|
+
#
|
368
|
+
def save
|
369
|
+
update_or_create
|
370
|
+
end
|
371
|
+
|
372
|
+
#Returns whether or not this is a dirty object or not
|
373
|
+
def dirty?
|
374
|
+
dirty
|
375
|
+
end
|
376
|
+
|
377
|
+
def color_cell(name,const)
|
378
|
+
#first, get the cell name
|
379
|
+
letter = self.class.columns[name.to_sym]
|
380
|
+
#get the sheet name, if we don't already have it
|
381
|
+
@wksheet = @@workbook.worksheets(self.class.to_s.tableize) if !@wksheet
|
382
|
+
#color the cell
|
383
|
+
@wksheet.Range("#{letter}#{self.id}").Interior.ColorIndex = const
|
384
|
+
end
|
385
|
+
|
386
|
+
def get_cell_color(name)
|
387
|
+
#get the cell name
|
388
|
+
letter = self.class.columns[name.to_sym]
|
389
|
+
@wksheet = @@workbook.worksheets(self.class.to_s.tableize) if !@wksheet
|
390
|
+
#get the cell color
|
391
|
+
@wksheet.Range("#{letter}#{self.id}").Interior.ColorIndex
|
392
|
+
end
|
393
|
+
|
394
|
+
private
|
395
|
+
#This method will determine whether to update an existing record
|
396
|
+
#or create a brand new one
|
397
|
+
def update_or_create
|
398
|
+
res = (is_new == true) ? create : update
|
399
|
+
res != false
|
400
|
+
end
|
401
|
+
|
402
|
+
#This method will create a brand new row in the sheet
|
403
|
+
def create
|
404
|
+
#determine the last row in the table
|
405
|
+
row = self.class.wksheet.Cells.CurrentRegion.Rows.Count
|
406
|
+
row += 1 #add 1 to the row to point to the next empty row
|
407
|
+
self.class.columns.each_pair do |k,v|
|
408
|
+
#k: will be the field name
|
409
|
+
#v: will be the column letter
|
410
|
+
self.class.wksheet.Range("#{v}#{row}").Value = self.send(k)
|
411
|
+
|
412
|
+
end
|
413
|
+
self.dirty = false
|
414
|
+
self.is_new = false
|
415
|
+
self.id = row
|
416
|
+
end
|
417
|
+
|
418
|
+
#this method will save the existing record
|
419
|
+
def update
|
420
|
+
self.class.columns.each_pair do |k,v|
|
421
|
+
#k: will be the field name
|
422
|
+
#v: will be the column letter
|
423
|
+
self.class.wksheet.Range("#{v}#{self.id}").Value = self.send(k)
|
424
|
+
end
|
425
|
+
self.dirty = false
|
426
|
+
end
|
427
|
+
|
428
|
+
|
429
|
+
|
430
|
+
end #End of Root class
|
431
|
+
|
432
|
+
end #End of Module
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require File.dirname(__FILE__)+ "/../lib/active_excel"
|
3
|
+
|
4
|
+
#this is a subclass of ActiveExcel::Root, it will be used
|
5
|
+
#to help verify inherited behaviors and connections
|
6
|
+
class OtherRecord < ActiveExcel::Root
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class NoSheet < ActiveExcel::Root
|
11
|
+
set_sheet_name :other_records
|
12
|
+
end
|
13
|
+
|
14
|
+
class X12CarbonCopyTest < ActiveExcel::Root
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class TestActiveExcel < Test::Unit::TestCase
|
19
|
+
|
20
|
+
def setup
|
21
|
+
@wkbk = ActiveExcel::Root.workbook_connect({:wkbook=>"gxstestsubs_billing.xls"})
|
22
|
+
#ActiveExcel::Root.load_constants if !@const_loaded
|
23
|
+
#@const_loaded = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
puts "Closing down excel....."
|
28
|
+
ActiveExcel::Root.workbook_disconnect
|
29
|
+
ActiveExcel::Root.excel_disconnect
|
30
|
+
@wkbk = nil
|
31
|
+
GC.start
|
32
|
+
sleep(2)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_connect_to_workbook
|
36
|
+
#this test verifies that a workbook can be connected to by passing in a hash
|
37
|
+
#config = {:wkbook=>"gxstestsubs_billing.xls"}
|
38
|
+
#ActiveExcel::Root.workbook_connect(config)
|
39
|
+
assert_not_nil @wkbk
|
40
|
+
assert_equal 3,@wkbk.Worksheets.Count
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
#def test_connect_to_two_different_workbooks
|
46
|
+
# #this test verifies that ActiveExcel won't crash if two Excel workbooks are open.
|
47
|
+
# #the first one will be opened by the set up method, and the second will be a connection
|
48
|
+
# #to SmallCCTests_FEReq_FEPart_FERecip.xls
|
49
|
+
#
|
50
|
+
# #first, grab an OtherRecord object, this will store the first workbook in the object:
|
51
|
+
# orec = OtherRecord.find(3)
|
52
|
+
# #now, open up the second one.
|
53
|
+
# ActiveExcel::Root.workbook_connect({:wkbook=>"SmallCCTests_FEReq_FEPart_FERecip.xls"})
|
54
|
+
# ActiveExcel::Root.set_sheet_name :x12_carbon_copy_tests
|
55
|
+
# x12 = X12CarbonCopyTest.find(2)
|
56
|
+
# assert_not_equal(orec.this_workbook, x12.this_workbook )
|
57
|
+
# ActiveExcel::Root.set_sheet_name = nil
|
58
|
+
#end
|
59
|
+
|
60
|
+
def test_column_names
|
61
|
+
#ae = ActiveExcel::Root.new
|
62
|
+
h = ActiveExcel::Root.column_names("other_records")
|
63
|
+
0.upto(3) do |i|
|
64
|
+
assert_equal true,h.include?(:"Field#{i+1}")
|
65
|
+
end
|
66
|
+
assert_equal "A",ActiveExcel::Root.columns[:Field1]
|
67
|
+
assert_equal "B",ActiveExcel::Root.columns[:Field2]
|
68
|
+
assert_equal "C",ActiveExcel::Root.columns[:Field3]
|
69
|
+
assert_equal "D",ActiveExcel::Root.columns[:Field4]
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_using_a_subclass
|
73
|
+
oe = OtherRecord.new
|
74
|
+
h = oe.class.column_names
|
75
|
+
0.upto(3) do |i|
|
76
|
+
assert_equal true,h.include?(:"Field#{i+1}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_specifying_a_sheet_name
|
81
|
+
|
82
|
+
ns = NoSheet.new
|
83
|
+
#assert_equal "other_records",NoSheet.sheet_name
|
84
|
+
h = ns.class.column_names
|
85
|
+
0.upto(3) do |i|
|
86
|
+
assert_equal true,h.include?(:"Field#{i+1}")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_verify_that_attr_added_dynamically
|
91
|
+
orec = OtherRecord.new
|
92
|
+
orec.Field1 = "A new value"
|
93
|
+
orec.Field4 = "BLAH BLAH"
|
94
|
+
assert_equal "A new value",orec.Field1
|
95
|
+
assert_equal -1,orec.id
|
96
|
+
assert_equal nil,orec.Field2
|
97
|
+
assert_equal nil,orec.Field3
|
98
|
+
assert_equal "BLAH BLAH",orec.Field4
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_find_a_record_with_a_specific_id
|
102
|
+
|
103
|
+
#simply passing in an integer will cause it to find by id
|
104
|
+
orec = OtherRecord.find(3)
|
105
|
+
assert_equal "Value2",orec.Field1
|
106
|
+
assert_equal "Value10",orec.Field2
|
107
|
+
assert_equal "Value18",orec.Field3
|
108
|
+
assert_equal "Value26",orec.Field4
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_find_by_attr_name
|
112
|
+
orec = OtherRecord.find_by_Field2("Value11")
|
113
|
+
assert_equal 4,orec.id
|
114
|
+
assert_equal "Value3",orec.Field1
|
115
|
+
assert_equal "Value11",orec.Field2
|
116
|
+
assert_equal "Value19",orec.Field3
|
117
|
+
assert_equal "Value27",orec.Field4
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_find_all_by_attr_name
|
121
|
+
orecs = OtherRecord.find_all_by_Field1("Value8")
|
122
|
+
assert_equal 2,orecs.size
|
123
|
+
orec1 = orecs[0]
|
124
|
+
orec2 = orecs[1]
|
125
|
+
assert_equal "Value8",orec1.Field1
|
126
|
+
assert_equal "Value16",orec1.Field2
|
127
|
+
assert_equal "Value8", orec2.Field1
|
128
|
+
assert_equal "Value160",orec2.Field2
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_find_by_attr_name_with_underscore
|
132
|
+
orec = OtherRecord.find_by_Field_Five("Value35")
|
133
|
+
assert_equal "Value3",orec.Field1
|
134
|
+
assert_equal "Value11",orec.Field2
|
135
|
+
assert_equal "Value19",orec.Field3
|
136
|
+
assert_equal "Value27",orec.Field4
|
137
|
+
assert_equal "Value35",orec.Field_Five
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_save_an_edited_record
|
141
|
+
orec = OtherRecord.find_by_Field3("Value23")
|
142
|
+
orec.Field1="UPDATED"
|
143
|
+
orec.Field2="RECORD"
|
144
|
+
orec.save
|
145
|
+
|
146
|
+
teardown()
|
147
|
+
#sleep(5) #go to sleep for 5 seconds so I can watch the taskmanager
|
148
|
+
|
149
|
+
#open it back up now
|
150
|
+
ActiveExcel::Root.workbook_connect({:wkbook=>"gxstestsubs_billing.xls"})
|
151
|
+
|
152
|
+
uprec = OtherRecord.find_by_Field3("Value23")
|
153
|
+
assert_equal "UPDATED",uprec.Field1
|
154
|
+
assert_equal "RECORD",uprec.Field2
|
155
|
+
|
156
|
+
#change them again and ensure that the object has been marked dirty.
|
157
|
+
uprec.Field1 = "Value7"
|
158
|
+
uprec.Field2 = "Value15"
|
159
|
+
|
160
|
+
assert_equal true,uprec.dirty?
|
161
|
+
uprec.save
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_find_all_records_in_sheet
|
165
|
+
recs = OtherRecord.find(:all)
|
166
|
+
assert_equal 9,recs.size
|
167
|
+
0.upto(7) do |i|
|
168
|
+
assert_equal "OtherRecord",recs[i].class.to_s
|
169
|
+
assert_equal "Value#{i+1}",recs[i].send(:"Field1")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_colorize_cell
|
174
|
+
#this verifies that we can color a cell using an excel constant
|
175
|
+
oe = OtherRecord.find(4)
|
176
|
+
oe.color_cell("Field1",ExcelConstants::XLGREEN)
|
177
|
+
oe.color_cell("Field3",ExcelConstants::XLRED)
|
178
|
+
oe.save
|
179
|
+
end
|
180
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ActiveExcel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rob Whitener
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-02 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.0
|
24
|
+
version:
|
25
|
+
description: Excel as the container of model objects
|
26
|
+
email: robwhitener@gmail.com
|
27
|
+
executables:
|
28
|
+
- active_excel
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
files:
|
36
|
+
- History.txt
|
37
|
+
- Manifest.txt
|
38
|
+
- README.txt
|
39
|
+
- Rakefile
|
40
|
+
- bin/active_excel
|
41
|
+
- lib/active_excel.rb
|
42
|
+
- test/test_active_excel.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://robsexocortex.blogspot.com/2009/06/activeexcel.html
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --main
|
50
|
+
- README.txt
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: active_excel
|
68
|
+
rubygems_version: 1.3.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Use Excel to contain model objects. Each row in a sheet represents an object, each sheet in a workbook represents a class. Make sure that you rename your sheets - this gem uses ActiveSupport to take advantage of the pluralization familiar to Rails developers. Modeled liberally on ActiveRecord, not quite as cool but hopefully useful enough. Cheers.
|
72
|
+
test_files:
|
73
|
+
- test/test_active_excel.rb
|