Created
October 6, 2010 13:53
-
-
Save todesking/613377 to your computer and use it in GitHub Desktop.
Wrapper for Excel OLE object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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{@[email protected]} | |
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