Skip to content

Instantly share code, notes, and snippets.

@todesking
Created October 6, 2010 13:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save todesking/613377 to your computer and use it in GitHub Desktop.
Save todesking/613377 to your computer and use it in GitHub Desktop.
Wrapper for Excel OLE object
require 'win32ole'
require 'kconv'
module Excel
class OLEWrapper
def initialize obj
@obj=obj
end
def raw_obj
@obj
end
end
class Application < OLEWrapper
def initialize
super WIN32OLE.new('Excel.Application')
ObjectSpace.define_finalizer(self,Application.finalizer_callback(self))
end
def self.finalizer_callback instance
lambda {
$stderr.puts "WARNING: Call Excel::Application#close !!"
instance.close
instance=nil
}
end
def close
@obj.Quit
ObjectSpace.undefine_finalizer self
end
def open path
Util.with_win32ole_exception_wrap {
WorkBook.new @obj.WorkBooks.Open(Util.encode_to_sjis(path))
}
end
def workbooks
OLEEnumerableWrapper.new @obj.WorkBooks
end
end
class OLEEnumerableWrapper < OLEWrapper
include Enumerable
def initialize obj,&block
super obj
@wrapper=block||lambda{|x|x}
end
def each
@obj.each {|elem|
yield @wrapper.call(elem)
}
end
def count
@obj.Count
end
alias length count
def [] index
@wrapper.call @obj.Item(index+1)
end
end
class WorkBook < OLEWrapper
def initialize obj
super obj
end
def sheets
OLEEnumerableWrapper.new(@obj.WorkSheets){|sheet|
WorkSheet.new sheet
}
end
end
class WorkSheet < OLEWrapper
def initialize obj
super obj
end
def name
Util.encode_to_kcode(@obj.Name)
end
def region
return Region.new self
end
def [] row_index,col_index
return Cell.new @obj.Cells(row_index,col_index)
end
def name
return Util.encode_to_kcode(@obj.Name||'')
end
end
class Region
def initialize worksheet
@worksheet=worksheet
@top=1
@left='A'
@right=nil
@bottom=nil
end
def top value
@top=value
self
end
def bottom value=nil,&cond
case value
when Symbol
case value
when :until_empty
@bottom=lambda{|cols|cols.all?{|col|col.text.empty?}}
else
raise "Unsupported condition: #{value}"
end
else
@bottom=value||cond
end
self
end
def left value
@left=value
self
end
def right value=nil,&cond
case value
when Symbol
case value
when :until_empty
@right=lambda{|cols|cols.all?{|col|col.text.empty?}}
else
raise "Unsupported condition: #{value}"
end
else
@right=value||cond
end
self
end
def width w
raise 'left is not set' unless @left
@right=@left
(w-1).times{@right=@right.succ}
self
end
def rows
return RowEnumerator.new self
end
def each_row &block
if @bottom.respond_to?(:call) && @right.respond_to?(:call)
raise "You can supply block to only right or bottom, not both"
end
l=Util.col_index_to_int(@left)
r=if @right.respond_to?(:call)
i=@left
r=i
cols=(@top..@bottom).map{|row| @worksheet[row,i]}
until @right.call(cols)
r=i
i=i.succ
cols=(@top..@bottom).map{|row| @worksheet[row,i]}
end
r
else
Util.col_index_to_int(@right)
end
i=@top
row=(l..r).map{|col| @worksheet[i,col]}
until @bottom.respond_to?(:call) ? @bottom.call(row) : i > @bottom
yield row
i+=1
row=(l..r).map{|col| @worksheet[i,col]}
end
end
end
class RowEnumerator
include Enumerable
def initialize region
@region=region
end
def each &block
return self unless block_given?
@region.each_row &block
end
end
class Cell < OLEWrapper
def initialize obj
super obj
end
def text
Util.encode_to_kcode(@obj.Value.to_s||'')
end
end
# TODO: unused
class Engine
def initialize
@app=WIN32OLE.new('Excel.Application')
end
attr_reader :app
end
class Util
def self.current_dir
file_system = WIN32OLE.new('Scripting.FileSystemObject')
file_system.getFolder('.').path
end
def self.abs_path path
File.join(self.current_dir,path)
end
def self.encode_to_kcode str,enc=Kconv::SJIS
enc||=Kconv.guess str
Kconv.kconv(str,kconv_encoding_id($KCODE),enc)
end
def self.encode_to_sjis str,enc=kconv_encoding_id($KCODE)
enc||=Kconv.guess str
Kconv.kconv(str,Kconv::SJIS,enc)
end
def self.kconv_encoding_id encoding_name
case encoding_name
when 'UTF8'
Kconv::UTF8
when 'SJIS'
Kconv::SJIS
when 'EUC'
Kconv::EUC
when 'NONE'
Kconv::NOCONV
else
raise "wtf: Unknown encoding name: #{encoding_name}"
end
end
def self.with_win32ole_exception_wrap
begin
yield
rescue WIN32OLERuntimeError
# FIXME: missing stack trace
ex=$!
raise WIN32OLERuntimeError.new encode_to_kcode(ex.message)
end
end
def self.col_index_to_int index
return index if index.kind_of? Integer
raise 'Bad col index format' unless index =~ /^[A-Z]{1,2}$/
bias='A'[0]
case index.length
when 1
return index[0]-bias+1
when 2
return (index[0]-bias+1)*26+index[1]-bias+1
else
raise 'ASSERTION ERROR'
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment