RobustExcelOle
This library automates reading, modifying, and writing Excel workbooks. It is designed to support several kinds of concurrency of both simultaneously running Excel instances and simultanously happening user interactions. RobustExcelOle deals with various cases of Excel and user behaviour, and implements workarounds for some Excel bugs. The gem provides convenient methods for common tasks, and facilitates referenced libraries.
RobustExcelOle works by sending VBA methods via Win32OLE. It keeps track of Excel workbooks and Excel instances.
Requirements
-
Ruby 1.8.6 or higher
Installation
You can install the gem by running the command
gem install robust_excel_ole
or by using bundler and putting the following line in your Gemfile.
gem 'robust_excel_ole'
Usage
RobustExcelOle can be used either for scripts
require 'robust_excel_ole'
include RobustExcelOle
or as a console
reo
The call of the console will include RobustExcelOle for you.
The following examples can be used for both scripts and console. If you are in the path of the gem and start the console, you can just put these examples.
Description
In the following example, we want to open a workbook, modify a cell, save and close the workbook.
Let’s open a workbook.
workbook = Workbook.open 'spec/data/workbook.xls'
Now we have a Workbook object that wraps a win32ole object. That is, you can send any win32ole (VBA) method to it. For example, you can determine the name and the saved-status of the workbook.
workbook.Name
# => "workbook.xls"
workbook.Visible
# => false
For some common tasks and for considering various complex cases of Excel and user behaviour, more convenient methods are implemented. For example, RobustExcelOle provides methods for reading and writing the contents of ranges, for opening, saving, closing, reopening and unobtrusively opening workbooks, and for setting options.
First we want to make the workbook visible.
workbook.visible = true
This method makes both the Excel instance and the window of the Workbook-win32ole-object visible.
Let’s read the value of a named cell.
value = workbook['firstcell']
# => "foo"
Now we want to write a new value into this cell.
workbook['firstcell'] = "new"
Then we’ll save the workbook.
workbook.save
Finally we want to close the workbook.
workbook.close
One special feature of RobustExcelOle is that it enables reopening workbooks.
workbook.reopen
The workbook is now open again and ready for applying further operations, e.g.
workbook['firstcell'] = "another_value"
workbook.save
workbook.close
RobustExcelOle allows unobtrusively reading and modifying workbooks, i.e. accessing workbooks without changing their “status”. The status comprises whether the workbook is open or closed, saved or unsaved, read-only or writable, visible or invisible, whether the calculation mode is manual or automatic, and checking compatibility is done or not done (the Workbook object remembers its properties).
Workbook.('spec/data/workbook.xls') do |workbook|
# do something
end
Workbook.for_reading('spec/data/workbook.xls') do |workbook|
# do something
end
More features when opening, modifying, saving and closing workbooks
We can open the workbook using a block, similar to, e.g., File.open
.
Workbook.open('spec/data/workbook.xls') do |workbook|
# do something
end
Once we have a workbook, we can set some options, e.g.
workbook.for_this_workbook(:visible => true, :read_only => false)
We can also open the workbook and provide the options in one step, e.g.
workbook = Workbook.open('spec/data/workbook.xls', :visible => true)
or, using abbreviations,
workbook = Workbook.open('spec/data/workbook.xls', :v => true)
For more details about opening workbooks see README_open
We can do a simple save
workbook.save
or save the workbook under a different name.
workbook.save_as('spec/data/new_workbook.xls')
Finally we can save and close the workbook in one step.
workbook.close(:if_unsaved => :save)
For more details about saving and closing workbooks see README_save_close
Using Excel instances
We can start a new Excel instance with
excel1 = Excel.create
or
excel1 = Excel.new(:reuse => false)
We can also obtain an Excel object by connecting to the already running Excel instance.
excel2 = Excel.current
or
excel2 = Excel.new(:reuse => true)
We close the Excel instance using
excel1.close
Closed Excel instances can be reopened.
excel1.recreate(:reopen_workbooks => true, :visible => true)
Closing all Excel instances is done by
Excel.close_all(:if_unsaved => :forget)
For hard terminating all Excel processes we can use
Excel.kill_all
For more details about creating Excel instances see README_excel
Opening workbooks in several Excel instances
RobustExcelOle enables opening and processing workbooks in several Excel instances. Using more than one Excel process allows, e.g., running a script that operates in one Excel instance, while a user (or another script) modifies workbooks in another Excel instance.
For example, suppose we want to open a workbook.
workbook1 = Workbook.open('spec/data/workbook.xls')
Now we want to open another workbook in a different Excel instance.
workbook2 = Workbook.open('spec/data/different_workbook.xls', :excel => :new)
We can also create a third Excel instance and open another workbook in this instance.
excel1 = Excel.create
book3 = Workbook.open('spec/data/another_workbook.xls', :excel => excel1)
A workbook is opened by default in the Excel instance where it was open before most recently.
book1.close
book1 = Workbook.open('spec/data/workbook.xls')
If this Excel instance is damaged or closed, then options control whether the workbook shall be opened in the current (active), a new or a given Excel instance.
workbook1 = Workbook.open('spec/data/workbook.xls', :default => {:excel => :new})
Without the option :default
, the workbook can be forced to be opened in the current, new or given Excel instance, no matter if and where it was opened before, e.g.
workbook2 = Workbook.open('spec/data/workbook.xls', :excel => excel1)
As a further feature, RobustExcelOle allows processing workbooks, while still supporting user’s interactions: The commands enable to open, close, reopen, read, modify, write and save Excel workbooks, without the need of the user’s interaction, and even without the user noticing. Thus, while running a script containing RobustExcelOle commands, the user can open and process workbooks in any Excel instances at any time. RobustExcelOle manages the complex cases of conflicts that might occur such that the user does not need to interfere and the script can continue.
For example, suppose we want to process a list of workbooks. RobustExcelOle allows to rapidly open, manipulate, close and save these workbooks. Now assume, the workbook “workbook.xls” is being processed, while the user has already opened this workbook, has modified but not saved it yet. Excel would prompt a message and ask the user what to do. RobustExcelOle solves this conflict by using an option that states whether the changes of the user should be saved (accepted) or discarded (forgotten) before opening the workbook, e.g.
workbook = Workbook.open('workbook.xls', :if_unsaved => :accept)
Similarly, if the user has opened a workbook that has the same name but a different path, the conflict can be solved via an option.
workbook1 = Workbook.open('spec/data/workbook.xls')
# do something
workbook2 = Workbook.open('spec/data/more/workbook.xls', :if_obstructed => :forget)
For more details about opening and closing workbooks in Excel instances see README_open
Operating on worksheets
Assume we have opened a workbook
workbook = Workbook.open('spec/data/workbook.xls')
We access the first worksheet by
sheet = workbook.sheet(1)
or
sheet = workbook.sheet('Sheet1')
or
sheet = workbook.first_sheet
We can read and change the worksheet name.
sheet.name
# => "Sheet1"
sheet.name = "new_sheet"
We can copy the first worksheet, name it and add it before the third worksheet.
workbook.add_or_copy_sheet(sheet, :as => "copied_name, :before => workbook.last_sheet)
For more details about processing worksheets see README_sheet
Reading and writing ranges in worksheets
We can read the first three cells of the first row
sheet.row_range(1, 1..3).values # => ["foo","workbook","sheet1"]
and the third column
sheet.col_range(3).values # => ["sheet1", 2.0, 4.0]
We can read the first cell, using
sheet[1,1].value # => "foo"
or
sheet.row_range(1)[0].value # => "foo"
Then we modify it.
sheet[1,1] = "hello"
We get the value of a named range
sheet["firstcell"] # => "hello"
and set another value to that range.
sheet["firstcell"] = "new_value"
For more details about reading and writing contents of cells and ranges see README_ranges
Examples
You can run the examples included in the directory examples
, e.g.
ruby examples\open_save_close\example_unobtrusively.rb
Development
This project RobustExcelOle is work in progress. We are happy to implement further features. So we invite you to send your pull requests. We then strive to realize them as soon as possible. If you have any feedback, or you find use cases that RobustExcelOle does not satisfy, please let us know.
RobustExcelOle is being tested for Excel 2010. It can be used for any recent Excel Office version.
The tests of RobustExcelOle are optimised with help of the rcov tool.
Support
Please contact us and to report issues and feature requests to github Issues. github.com/Thomas008/robust_excel_ole/issues
Collaborate
Please pull request on github.
Author
thomas [email protected]
License
MIT License. For more imformation, please see LICENSE.