<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre, #msg p { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[1950] branches/service-updates-and-curation: Added missing ruby-ole gem (
 that is required by the spreadsheet gem).</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd>1950</dd>
<dt>Author</dt> <dd>jits</dd>
<dt>Date</dt> <dd>2010-05-12 12:46:04 -0400 (Wed, 12 May 2010)</dd>
</dl>

<h3>Log Message</h3>
<pre>Added missing ruby-ole gem (that is required by the spreadsheet gem).</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesserviceupdatesandcurationconfigenvironmentrb">branches/service-updates-and-curation/config/environment.rb</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101specification">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/.specification</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101ChangeLog">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/ChangeLog</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101README">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/README</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101Rakefile">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/Rakefile</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101binoletool">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/oletool</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/data/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101datapropidsyaml">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/data/propids.yaml</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/</li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolebaserb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/base.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolefile_systemrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/file_system.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101liboleranges_iorb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/ranges_io.rb</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragebaserb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/base.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragefile_systemrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/file_system.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragemeta_datarb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/meta_data.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragerb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101libolesupportrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/support.rb</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesbaserb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/base.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesproperty_setrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/property_set.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types.rb</a></li>
<li>branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/</li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testoleWithDirsole">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/oleWithDirs.ole</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtestdoc">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test.doc</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_SummaryInformation">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_SummaryInformation</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_filesystemrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_filesystem.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_mbatrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_mbat.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_meta_datarb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_meta_data.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_property_setrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_property_set.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_ranges_iorb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_ranges_io.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_storagerb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_storage.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_supportrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_support.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_typesrb">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_types.rb</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_6doc">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_6.doc</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_95doc">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_95.doc</a></li>
<li><a href="#branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_97doc">branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_97.doc</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesserviceupdatesandcurationconfigenvironmentrb"></a>
<div class="modfile"><h4>Modified: branches/service-updates-and-curation/config/environment.rb (1949 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/config/environment.rb        2010-05-12 16:31:03 UTC (rev 1949)
+++ branches/service-updates-and-curation/config/environment.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx">   config.gem 'factory_girl', :version =&gt; '1.2.3', :source =&gt; &quot;http://gemcutter.org&quot;
</span><span class="cx">   config.gem 'mocha', :version =&gt; '0.9.8'
</span><span class="cx">   config.gem 'seer', :version =&gt; '0.6.1'
</span><ins>+  config.gem 'ruby-ole', :version =&gt; '1.2.10.1', :lib =&gt; 'ole/base'
</ins><span class="cx">   config.gem 'spreadsheet', :version =&gt; '0.6.4.1'
</span><span class="cx"> 
</span><span class="cx">   # Only load the plugins named here, in the order given. By default, all plugins 
</span></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101specification"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/.specification (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/.specification                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/.specification        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,65 @@
</span><ins>+--- !ruby/object:Gem::Specification 
+name: ruby-ole
+version: !ruby/object:Gem::Version 
+  prerelease: false
+  segments: 
+  - 1
+  - 2
+  - 10
+  - 1
+  version: 1.2.10.1
+platform: ruby
+authors: []
+
+autorequire: 
+bindir: bin
+cert_chain: []
+
+date: 2010-05-12 00:00:00 +01:00
+default_executable: 
+dependencies: []
+
+description: 
+email: 
+executables: []
+
+extensions: []
+
+extra_rdoc_files: []
+
+files: 
+- lib
+- lib/ole
+has_rdoc: true
+homepage: 
+licenses: []
+
+post_install_message: 
+rdoc_options: []
+
+require_paths: 
+- bin
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement 
+  requirements: 
+  - - &quot;&gt;=&quot;
+    - !ruby/object:Gem::Version 
+      segments: 
+      - 0
+      version: &quot;0&quot;
+required_rubygems_version: !ruby/object:Gem::Requirement 
+  requirements: 
+  - - &quot;&gt;=&quot;
+    - !ruby/object:Gem::Version 
+      segments: 
+      - 0
+      version: &quot;0&quot;
+requirements: []
+
+rubyforge_project: 
+rubygems_version: 1.3.6
+signing_key: 
+specification_version: 3
+summary: 
+test_files: []
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101ChangeLog"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/ChangeLog (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/ChangeLog                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/ChangeLog        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+== 1.2.10 / 2009-07-20
+
+- Mostly more performance enhancements, significantly faster for
+  certain operations.
+- Using lots of files is faster due to new hash lookup for dirents by name.
+- Writes of many files are faster now too as Dirent &amp; FileTime serialization
+  has been improved.
+- Certain operations from the filesystem api have been profiled and sped up.
+- Don't use syswrite on jruby to avoid the buffered stream warnings.
+
+== 1.2.9 / 2009-07-14
+
+- Lots of performance enhancements for RangesIO.
+
+== 1.2.8.2 / 2009-01-01
+
+- Update code to support ruby 1.9.1
+
+== 1.2.8.1 / 2008-10-22
+
+- Fix a couple of breakages when using $KCODE = 'UTF8'
+
+== 1.2.8 / 2008-10-08
+
+- Check in the new fixes to the mbat support.
+- Update README to be a bit more useful.
+
+== 1.2.7 / 2008-08-12
+
+- Prepare Ole::Types::PropertySet for write support.
+- Introduce Ole::Storage#meta_data as an easy interface to meta data stored
+  within various property sets.
+- Add new --metadata action to oletool to dump said metadata.
+- Add new --mimetype action to oletool, and corresponding Ole::Storage#mime_type
+  function to try to guess mime type of a file based on some simple heuristics.
+- Restructure project files a bit, and pull in file_system &amp; meta_data support
+  by default.
+- More tests - now have 100% coverage.
+
+== 1.2.6 / 2008-07-21
+
+- Fix FileClass#expand_path to work properly on darwin (issue #2)
+- Guard against Enumerable#sum clash with active support (issue #3)
+
+== 1.2.5 / 2008-02-16
+
+- Make all tests pass on ruby 1.9.
+
+== 1.2.4 / 2008-01-09
+
+- Make all tests pass on windows (issue #1).
+- Make all tests pass on a power pc (running ubuntu).
+- Property set convenience access functions.
+
+== 1.2.3 / 2007-12-28
+
+- MBAT write support re-implmented. Can now write files over ~8mb again.
+- Minor fixes (truncation in #flush, file modification timestamps)
+- More test coverage
+- Initial (read-only) property set support.
+- Complete filesystem api, to pass most of the rubyzip tests.
+- Add a ChangeLog :).
+
+== 1.2.2 / 2007-11-05
+
+- Lots of test updates, 90% coverage.
+- Fix +to_tree+ method to be more efficient, and stream output.
+- Optimizations from benchmarks and profiling, mostly for writes. Fixed
+  AllocationTable#resize_chain, RangesIOResizable#truncate and
+  AllocationTable#free_block.
+- Add in filesystem test file from rubyzip, and start working on a
+  filesystem api.
+
+== 1.2.1 / 2007-08-20
+
+- Separate out from ruby-msg as new project.
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101README"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/README (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/README                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/README        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,117 @@
</span><ins>+= Introduction
+
+The ruby-ole library provides a variety of functions primarily for
+working with OLE2 structured storage files, such as those produced by
+Microsoft Office - eg *.doc, *.msg etc.
+
+= Example Usage
+
+Here are some examples of how to use the library functionality,
+categorised roughly by purpose.
+
+1. Reading and writing files within an OLE container
+
+   The recommended way to manipulate the contents is via the
+   &quot;file_system&quot; API, whereby you use Ole::Storage instance methods
+   similar to the regular File and Dir class methods.
+
+     ole = Ole::Storage.open('oleWithDirs.ole', 'rb+')
+     p ole.dir.entries('.') # =&gt; [&quot;.&quot;, &quot;..&quot;, &quot;dir1&quot;, &quot;dir2&quot;, &quot;file1&quot;]
+     p ole.file.read('file1')[0, 25] # =&gt; &quot;this is the entry 'file1'&quot;
+     ole.dir.mkdir('newdir')
+
+2. Accessing OLE meta data
+
+   Some convenience functions are provided for (currently read only)
+   access to OLE property sets and other sources of meta data.
+
+     ole = Ole::Storage.open('test_word_95.doc')
+     p ole.meta_data.file_format # =&gt; &quot;MSWordDoc&quot;
+     p ole.meta_data.mime_type # =&gt; &quot;application/msword&quot;
+     p ole.meta_data.doc_author.split.first # =&gt; &quot;Charles&quot;
+
+3. Raw access to underlying OLE internals
+
+   This is probably of little interest to most developers using the
+   library, but for some use cases you may need to drop down to the
+   lower level API on which the &quot;file_system&quot; API is constructed,
+   which exposes more of the format details.
+
+   &lt;tt&gt;Ole::Storage&lt;/tt&gt; files can have multiple files with the same name,
+   or with a slash in the name, and other things that are probably
+   strictly invalid. This API is the only way to access those files.
+
+   You can access the header object directly:
+
+     p ole.header.num_sbat # =&gt; 1
+     p ole.header.magic.unpack('H*') # =&gt; [&quot;d0cf11e0a1b11ae1&quot;]
+
+   You can directly access the array of all Dirent objects,
+   including the root:
+
+     p ole.dirents.length # =&gt; 5
+     puts ole.root.to_tree
+     # =&gt;
+     - #&lt;Dirent:&quot;Root Entry&quot;&gt;
+       |- #&lt;Dirent:&quot;\001Ole&quot; size=20 data=&quot;\001\000\000\002\000...&quot;&gt;
+       |- #&lt;Dirent:&quot;\001CompObj&quot; size=98 data=&quot;\001\000\376\377\003...&quot;&gt;
+       |- #&lt;Dirent:&quot;WordDocument&quot; size=2574 data=&quot;\334\245e\000-...&quot;&gt;
+       \- #&lt;Dirent:&quot;\005SummaryInformation&quot; size=54788 data=&quot;\376\377\000\000\001...&quot;&gt;
+
+   You can access (through RangesIO methods, or by using the
+   relevant Dirent and AllocationTable methods) information like where within
+   the container a stream is located (these are offset/length pairs):
+
+     p ole.root[&quot;\001CompObj&quot;].open { |io| io.ranges } # =&gt; [[0, 64], [64, 34]]
+
+See the documentation for each class for more details.
+
+= Thanks
+
+* The code contained in this project was initially based on chicago's libole
+  (source available at http://prdownloads.sf.net/chicago/ole.tgz).
+
+* It was later augmented with some corrections by inspecting pole, and (purely
+  for header definitions) gsf.
+
+* The property set parsing code came from the apache java project POIFS.
+
+* The excellent idea for using a pseudo file system style interface by providing
+  #file and #dir methods which mimic File and Dir, was borrowed (along with almost
+  unchanged tests!) from Thomas Sondergaard's rubyzip.
+
+= TODO
+
+== 1.2.11
+
+* internal api cleanup
+* add buffering to rangesio so that performance for small reads and writes
+  isn't so awful. maybe try and remove the bottlenecks of unbuffered first
+  with more profiling, then implement the buffering on top of that.
+* fix mode strings - like truncate when using 'w+', supporting append
+  'a+' modes etc. done?
+* make ranges io obey readable vs writeable modes.
+* more RangesIO completion. ie, doesn't support #&lt;&lt; at the moment.
+* maybe some oletool doc.
+* make sure `rake test' runs tests both with $KCODE='UTF8', and without,
+  and maybe ensure i don't regress on 1.9 and jruby either now that they're
+  fixed.
+
+== 1.3.1
+
+* case insensitive open mode would be nice
+* fix property sets a bit more. see TODO in Ole::Storage::MetaData
+* ability to zero out padding and unused blocks
+* case insensitive mode for ole/file_system?
+* better tests for mbat support.
+* further doc cleanup
+* add in place testing for jruby and ruby1.9
+
+== Longer term
+
+* more benchmarking, profiling, and speed fixes. was thinking vs other
+  ruby filesystems (eg, vs File/Dir itself, and vs rubyzip), and vs other
+  ole implementations (maybe perl's, and poifs) just to check its in the
+  ballpark, with no remaining silly bottlenecks.
+* supposedly vba does something weird to ole files. test that.
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101Rakefile"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/Rakefile (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/Rakefile                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/Rakefile        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,116 @@
</span><ins>+require 'rake/rdoctask'
+require 'rake/testtask'
+require 'rake/packagetask'
+require 'rake/gempackagetask'
+
+require 'rbconfig'
+require 'fileutils'
+
+$:.unshift 'lib'
+
+require 'ole/storage'
+
+PKG_NAME = 'ruby-ole'
+PKG_VERSION = Ole::Storage::VERSION
+
+task :default =&gt; [:test]
+
+Rake::TestTask.new do |t|
+        t.test_files = FileList[&quot;test/test_*.rb&quot;]
+        t.warning = true
+        t.verbose = true
+end
+
+begin
+        require 'rcov/rcovtask'
+        # NOTE: this will not do anything until you add some tests
+        desc &quot;Create a cross-referenced code coverage report&quot;
+        Rcov::RcovTask.new do |t|
+                t.test_files = FileList['test/test*.rb']
+                t.ruby_opts &lt;&lt; &quot;-Ilib&quot; # in order to use this rcov
+                t.rcov_opts &lt;&lt; &quot;--xrefs&quot;  # comment to disable cross-references
+                t.verbose = true
+        end
+rescue LoadError
+        # Rcov not available
+end
+
+Rake::RDocTask.new do |t|
+        t.rdoc_dir = 'doc'
+        t.rdoc_files.include 'lib/**/*.rb'
+        t.rdoc_files.include 'README', 'ChangeLog'
+        t.title    = &quot;#{PKG_NAME} documentation&quot;
+        t.options += %w[--line-numbers --inline-source --tab-width 2]
+        t.main           = 'README'
+end
+
+spec = Gem::Specification.new do |s|
+        s.name = PKG_NAME
+        s.version = PKG_VERSION
+        s.summary = %q{Ruby OLE library.}
+        s.description = %q{A library for easy read/write access to OLE compound documents for Ruby.}
+        s.authors = ['Charles Lowe']
+        s.email = %q{aquasync@gmail.com}
+        s.homepage = %q{http://code.google.com/p/ruby-ole}
+        s.rubyforge_project = %q{ruby-ole}
+
+        s.executables = ['oletool']
+        s.files  = ['README', 'Rakefile', 'ChangeLog', 'data/propids.yaml']
+        s.files += FileList['lib/**/*.rb']
+        s.files += FileList['test/test_*.rb', 'test/*.doc']
+        s.files += FileList['test/oleWithDirs.ole', 'test/test_SummaryInformation']
+        s.files += FileList['bin/*']
+        s.test_files = FileList['test/test_*.rb']
+
+        s.has_rdoc = true
+        s.extra_rdoc_files = ['README', 'ChangeLog']
+        s.rdoc_options += [
+                '--main', 'README',
+                '--title', &quot;#{PKG_NAME} documentation&quot;,
+                '--tab-width', '2'
+        ]
+end
+
+Rake::GemPackageTask.new(spec) do |t|
+        t.gem_spec = spec
+        t.need_tar = false
+        t.need_zip = false
+        t.package_dir = 'build'
+end
+
+desc 'Run various benchmarks'
+task :benchmark do
+        require 'benchmark'
+        require 'tempfile'
+        require 'ole/file_system'
+
+        # should probably add some read benchmarks too
+        def write_benchmark opts={}
+                files, size = opts[:files], opts[:size]
+                block_size = opts[:block_size] || 100_000
+                block = 0.chr * block_size
+                blocks, remaining = size.divmod block_size
+                remaining = 0.chr * remaining
+                Tempfile.open 'ole_storage_benchmark' do |temp|
+                        Ole::Storage.open temp do |ole|
+                                files.times do |i|
+                                        ole.file.open &quot;file_#{i}&quot;, 'w' do |f|
+                                                blocks.times { f.write block }
+                                                f.write remaining
+                                        end
+                                end
+                        end
+                end
+        end
+
+        Benchmark.bm do |bm|
+                bm.report 'write_1mb_1x5' do
+                        5.times { write_benchmark :files =&gt; 1, :size =&gt; 1_000_000 }
+                end
+
+                bm.report 'write_1mb_2x5' do
+                        5.times { write_benchmark :files =&gt; 1_000, :size =&gt; 1_000 }
+                end
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101binoletool"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/oletool (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/oletool                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/oletool        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+#! /usr/bin/ruby
+
+require 'optparse'
+require 'ole/storage'
+
+def oletool
+        opts = {:verbose =&gt; false, :action =&gt; :tree}
+        op = OptionParser.new do |op|
+                op.banner = &quot;Usage: oletool [options] [files]&quot;
+                op.separator ''
+                op.on('-t', '--tree', 'Dump ole trees for files (default)') { opts[:action] = :tree }
+                op.on('-r', '--repack', 'Repack the ole files in canonical form') { opts[:action] = :repack }
+                op.on('-m', '--mimetype', 'Print the guessed mime types') { opts[:action] = :mimetype }
+                op.on('-y', '--metadata', 'Dump the internal meta data as YAML') { opts[:action] = :metadata }
+                op.separator ''
+                op.on('-v', '--[no-]verbose', 'Run verbosely') { |v| opts[:verbose] = v }
+                op.on_tail('-h', '--help', 'Show this message') { puts op; exit }
+        end
+        files = op.parse ARGV
+        if files.empty?
+                puts 'Must specify 1 or more msg files.'
+                puts op
+                exit 1
+        end
+        Ole::Log.level = opts[:verbose] ? Logger::WARN : Logger::FATAL
+        files.each do |file|
+                case opts[:action]
+                when :tree
+                        Ole::Storage.open(file) { |ole| puts ole.root.to_tree }
+                when :repack
+                        Ole::Storage.open file, 'rb+', &amp;:repack
+                when :metadata
+                        Ole::Storage.open(file) { |ole| y ole.meta_data.to_h }
+                when :mimetype
+                        puts Ole::Storage.open(file) { |ole| ole.meta_data.mime_type }
+                end
+        end
+end
+
+oletool
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/bin/oletool
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101datapropidsyaml"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/data/propids.yaml (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/data/propids.yaml                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/data/propids.yaml        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+&quot;{f29f85e0-4ff9-1068-ab91-08002b27b3d9}&quot;:
+  - FMTID_SummaryInformation
+  - 2:  doc_title
+    3:  doc_subject
+    4:  doc_author
+    5:  doc_keywords
+    6:  doc_comments
+    7:  doc_template
+    8:  doc_last_author
+    9:  doc_rev_number
+    10: doc_edit_time
+    11: doc_last_printed
+    12: doc_created_time
+    13: doc_last_saved_time
+    14: doc_page_count
+    15: doc_word_count
+    16: doc_char_count
+    18: doc_app_name
+    19: security
+
+&quot;{d5cdd502-2e9c-101b-9397-08002b2cf9ae}&quot;:
+  - FMTID_DocSummaryInfo
+  - 2:  doc_category
+    3:  doc_presentation_target
+    4:  doc_byte_count
+    5:  doc_line_count
+    6:  doc_para_count
+    7:  doc_slide_count
+    8:  doc_note_count
+    9:  doc_hidden_count
+    10: mmclips
+    11: scale_crop
+    12: heading_pairs
+    13: doc_part_titles
+    14: doc_manager
+    15: doc_company
+    16: links_up_to_date
+
+&quot;{d5cdd505-2e9c-101b-9397-08002b2cf9ae}&quot;:
+  - FMTID_UserDefinedProperties
+  - {}
+
+# just dumped these all here. if i can confirm any of these
+# better, i can update this file so they're recognized.
+#0b63e343-9ccc-11d0-bcdb-00805fccce04
+#0b63e350-9ccc-11d0-bcdb-00805fccce04 NetLibrary propset?
+#31f400a0-fd07-11cf-b9bd-00aa003db18e ScriptInfo propset?
+#49691c90-7e17-101a-a91c-08002b2ecda9 Query propset?
+#560c36c0-503a-11cf-baa1-00004c752a9a
+#70eb7a10-55d9-11cf-b75b-00aa0051fe20 HTMLInfo propset
+#85ac0960-1819-11d1-896f-00805f053bab message propset?
+#aa568eec-e0e5-11cf-8fda-00aa00a14f93 NNTP SummaryInformation propset?
+#b725f130-47ef-101a-a5f1-02608c9eebac Storage propset
+#c82bf596-b831-11d0-b733-00aa00a1ebd2 NetLibraryInfo propset
+#c82bf597-b831-11d0-b733-00aa00a1ebd2 LinkInformation propset?
+#d1b5d3f0-c0b3-11cf-9a92-00a0c908dbf1 LinkInformation propset?
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolebaserb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/base.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/base.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/base.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+
+require 'ole/support'
+
+module Ole # :nodoc:
+        Log = Logger.new_with_callstack
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolefile_systemrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/file_system.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/file_system.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/file_system.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,2 @@
</span><ins>+# keeping this file around for now, but will delete later on...
+require 'ole/storage/file_system'
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101liboleranges_iorb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/ranges_io.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/ranges_io.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/ranges_io.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,260 @@
</span><ins>+# need IO::Mode
+require 'ole/support'
+
+#
+# = Introduction
+#
+# +RangesIO+ is a basic class for wrapping another IO object allowing you to arbitrarily reorder
+# slices of the input file by providing a list of ranges. Intended as an initial measure to curb
+# inefficiencies in the Dirent#data method just reading all of a file's data in one hit, with
+# no method to stream it.
+# 
+# This class will encapuslate the ranges (corresponding to big or small blocks) of any ole file
+# and thus allow reading/writing directly to the source bytes, in a streamed fashion (so just
+# getting 16 bytes doesn't read the whole thing).
+#
+# In the simplest case it can be used with a single range to provide a limited io to a section of
+# a file.
+#
+# = Limitations
+#
+# * No buffering. by design at the moment. Intended for large reads
+# 
+# = TODO
+# 
+# On further reflection, this class is something of a joining/optimization of
+# two separate IO classes. a SubfileIO, for providing access to a range within
+# a File as a separate IO object, and a ConcatIO, allowing the presentation of
+# a bunch of io objects as a single unified whole.
+# 
+# I will need such a ConcatIO if I'm to provide Mime#to_io, a method that will
+# convert a whole mime message into an IO stream, that can be read from.
+# It will just be the concatenation of a series of IO objects, corresponding to
+# headers and boundaries, as StringIO's, and SubfileIO objects, coming from the
+# original message proper, or RangesIO as provided by the Attachment#data, that
+# will then get wrapped by Mime in a Base64IO or similar, to get encoded on-the-
+# fly. Thus the attachment, in its plain or encoded form, and the message as a
+# whole never exists as a single string in memory, as it does now. This is a
+# fair bit of work to achieve, but generally useful I believe.
+# 
+# This class isn't ole specific, maybe move it to my general ruby stream project.
+# 
+class RangesIO
+        attr_reader :io, :mode, :ranges, :size, :pos
+        # +io+:: the parent io object that we are wrapping.
+        # +mode+:: the mode to use
+        # +params+:: hash of params.
+        # * :ranges - byte offsets, either:
+        #   1. an array of ranges [1..2, 4..5, 6..8] or
+        #   2. an array of arrays, where the second is length [[1, 1], [4, 1], [6, 2]] for the above
+        #      (think the way String indexing works)
+        # * :close_parent - boolean to close parent when this object is closed
+        #
+        # NOTE: the +ranges+ can overlap.
+        def initialize io, mode='r', params={}
+                mode, params = 'r', mode if Hash === mode
+                ranges = params[:ranges]
+                @params = {:close_parent =&gt; false}.merge params
+                @mode = IO::Mode.new mode
+                @io = io
+                # initial position in the file
+                @pos = 0
+                self.ranges = ranges || [[0, io.size]]
+                # handle some mode flags
+                truncate 0 if @mode.truncate?
+                seek size if @mode.append?
+        end
+        
+        # add block form. TODO add test for this
+        def self.open(*args, &amp;block)
+                ranges_io = new(*args)
+                if block_given?
+                        begin;  yield ranges_io
+                        ensure; ranges_io.close
+                        end
+                else
+                        ranges_io
+                end
+        end
+
+        def ranges= ranges
+                # convert ranges to arrays. check for negative ranges?
+                ranges = ranges.map { |r| Range === r ? [r.begin, r.end - r.begin] : r }
+                # combine ranges
+                if @params[:combine] == false
+                        # might be useful for debugging...
+                        @ranges = ranges
+                else
+                        @ranges = []
+                        next_pos = nil
+                        ranges.each do |pos, len|
+                                if next_pos == pos
+                                        @ranges.last[1] += len
+                                        next_pos += len
+                                else
+                                        @ranges &lt;&lt; [pos, len]
+                                        next_pos = pos + len
+                                end
+                        end
+                end
+                # calculate cumulative offsets from range sizes
+                @size = 0
+                @offsets = []
+                @ranges.each do |pos, len|
+                        @offsets &lt;&lt; @size
+                        @size += len
+                end
+                self.pos = @pos
+        end
+
+        def pos= pos, whence=IO::SEEK_SET
+                case whence
+                when IO::SEEK_SET
+                when IO::SEEK_CUR
+                        pos += @pos
+                when IO::SEEK_END
+                        pos = @size + pos
+                else raise Errno::EINVAL
+                end
+                raise Errno::EINVAL unless (0..@size) === pos
+                @pos = pos
+
+                # do a binary search throuh @offsets to find the active range.
+                a, c, b = 0, 0, @offsets.length
+                while a &lt; b
+                        c = (a + b) / 2
+                        pivot = @offsets[c]
+                        if pos == pivot
+                                @active = c
+                                return
+                        elsif pos &lt; pivot
+                                b = c
+                        else
+                                a = c + 1
+                        end
+                end
+
+                @active = a - 1
+        end
+
+        alias seek :pos=
+        alias tell :pos
+
+        def rewind
+                seek 0
+        end
+
+        def close
+                @io.close if @params[:close_parent]
+        end
+
+        def eof?
+                @pos == @size
+        end
+
+        # read bytes from file, to a maximum of +limit+, or all available if unspecified.
+        def read limit=nil
+                data = ''
+                return data if eof?
+                limit ||= size
+                pos, len = @ranges[@active]
+                diff = @pos - @offsets[@active]
+                pos += diff
+                len -= diff
+                loop do
+                        @io.seek pos
+                        if limit &lt; len
+                                s = @io.read(limit).to_s
+                                @pos += s.length
+                                data &lt;&lt; s
+                                break
+                        end
+                        s = @io.read(len).to_s
+                        @pos += s.length
+                        data &lt;&lt; s
+                        break if s.length != len
+                        limit -= len
+                        break if @active == @ranges.length - 1
+                        @active += 1
+                        pos, len = @ranges[@active]
+                end
+                data
+        end
+
+        # you may override this call to update @ranges and @size, if applicable.
+        def truncate size
+                raise NotImplementedError, 'truncate not supported'
+        end
+
+        # using explicit forward instead of an alias now for overriding.
+        # should override truncate.
+        def size=        size
+                truncate size
+        end
+
+        def write data
+                return 0 if data.empty?
+                data_pos = 0
+                # if we don't have room, we can use the truncate hook to make more space.
+                if data.length &gt; @size - @pos
+                        begin
+                                truncate @pos + data.length
+                        rescue NotImplementedError
+                                raise IOError, &quot;unable to grow #{inspect} to write #{data.length} bytes&quot; 
+                        end
+                end
+                pos, len = @ranges[@active]
+                diff = @pos - @offsets[@active]
+                pos += diff
+                len -= diff
+                loop do
+                        @io.seek pos
+                        if data_pos + len &gt; data.length
+                                chunk = data[data_pos..-1]
+                                @io.write chunk
+                                @pos += chunk.length
+                                data_pos = data.length
+                                break
+                        end
+                        @io.write data[data_pos, len]
+                        @pos += len
+                        data_pos += len
+                        break if @active == @ranges.length - 1
+                        @active += 1
+                        pos, len = @ranges[@active]
+                end
+                data_pos
+        end
+        
+        alias &lt;&lt; write
+
+        # i can wrap it in a buffered io stream that
+        # provides gets, and appropriately handle pos,
+        # truncate. mostly added just to past the tests.
+        # FIXME
+        def gets
+                s = read 1024
+                i = s.index &quot;\n&quot;
+                self.pos -= s.length - (i+1)
+                s[0..i]
+        end
+        alias readline :gets
+
+        def inspect
+                &quot;#&lt;#{self.class} io=#{io.inspect}, size=#{@size}, pos=#{@pos}&gt;&quot;
+        end
+end
+
+# this subclass of ranges io explicitly ignores the truncate part of 'w' modes.
+# only really needed for the allocation table writes etc. maybe just use explicit modes
+# for those
+# better yet write a test that breaks before I fix it. added nodoc for the 
+# time being.
+class RangesIONonResizeable &lt; RangesIO # :nodoc:
+        def initialize io, mode='r', params={}
+                mode, params = 'r', mode if Hash === mode
+                flags = IO::Mode.new(mode).flags &amp; ~IO::TRUNC
+                super io, flags, params
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragebaserb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/base.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/base.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/base.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,948 @@
</span><ins>+require 'tempfile'
+
+require 'ole/base'
+require 'ole/types'
+require 'ole/ranges_io'
+
+module Ole # :nodoc:
+        #
+        # This class is the primary way the user interacts with an OLE storage file.
+        #
+        # = TODO
+        #
+        # * the custom header cruft for Header and Dirent needs some love.
+        # * i have a number of classes doing load/save combos: Header, AllocationTable, Dirent,
+        #   and, in a manner of speaking, but arguably different, Storage itself.
+        #   they have differing api's which would be nice to rethink.
+        #   AllocationTable::Big must be created aot now, as it is used for all subsequent reads.
+        #
+        class Storage
+                # thrown for any bogus OLE file errors.
+                class FormatError &lt; StandardError # :nodoc:
+                end
+
+                VERSION = '1.2.10.1'
+
+                # options used at creation time
+                attr_reader :params
+                # The top of the ole tree structure
+                attr_reader :root
+                # The tree structure in its original flattened form. only valid after #load, or #flush.
+                attr_reader :dirents
+                # The underlying io object to/from which the ole object is serialized, whether we
+                # should close it, and whether it is writeable
+                attr_reader :io, :close_parent, :writeable
+                # Low level internals, you probably shouldn't need to mess with these
+                attr_reader :header, :bbat, :sbat, :sb_file
+
+                # +arg+ should be either a filename, or an +IO+ object, and needs to be seekable.
+                # +mode+ is optional, and should be a regular mode string.
+                def initialize arg, mode=nil, params={}
+                        params, mode = mode, nil if Hash === mode
+                        params = {:update_timestamps =&gt; true}.merge(params)
+                        @params = params
+        
+                        # get the io object
+                        @close_parent, @io = if String === arg
+                                mode ||= 'rb'
+                                [true, open(arg, mode)]
+                        else
+                                raise ArgumentError, 'unable to specify mode string with io object' if mode
+                                [false, arg]
+                        end
+                        # do we have this file opened for writing? don't know of a better way to tell
+                        # (unless we parse the mode string in the open case)
+                        # hmmm, note that in ruby 1.9 this doesn't work anymore. which is all the more
+                        # reason to use mode string parsing when available, and fall back to something like
+                        # io.writeable? otherwise.
+                        @writeable = begin
+                                if mode
+                                        IO::Mode.new(mode).writeable?
+                                else
+                                        @io.flush
+                                        # this is for the benefit of ruby-1.9
+                                        # generates warnings on jruby though... :/
+                                        if RUBY_PLATFORM != 'java' and @io.respond_to?(:syswrite)
+                                                @io.syswrite('')
+                                        end
+                                        true
+                                end
+                        rescue IOError
+                                false
+                        end
+                        # silence undefined warning in clear
+                        @sb_file = nil
+                        # if the io object has data, we should load it, otherwise start afresh
+                        # this should be based on the mode string rather.
+                        @io.size &gt; 0 ? load : clear
+                end
+
+                # somewhat similar to File.open, the open class method allows a block form where
+                # the Ole::Storage object is automatically closed on completion of the block.
+                def self.open arg, mode=nil, params={}
+                        ole = new arg, mode, params
+                        if block_given?
+                                begin   yield ole
+                                ensure; ole.close
+                                end
+                        else ole
+                        end
+                end
+
+                # load document from file.
+                #
+                # TODO: implement various allocationtable checks, maybe as a AllocationTable#fsck function :)
+                #
+                # 1. reterminate any chain not ending in EOC.
+                #    compare file size with actually allocated blocks per file.
+                # 2. pass through all chain heads looking for collisions, and making sure nothing points to them
+                #    (ie they are really heads). in both sbat and mbat
+                # 3. we know the locations of the bbat data, and mbat data. ensure that there are placeholder blocks
+                #    in the bat for them.
+                # 4. maybe a check of excess data. if there is data outside the bbat.truncate.length + 1 * block_size,
+                #    (eg what is used for truncate in #flush), then maybe add some sort of message about that. it
+                #    will be automatically thrown away at close time.
+                def load
+                        # we always read 512 for the header block. if the block size ends up being different,
+                        # what happens to the 109 fat entries. are there more/less entries?
+                        @io.rewind
+                        header_block = @io.read 512
+                        @header = Header.new header_block
+
+                        # create an empty bbat.
+                        @bbat = AllocationTable::Big.new self
+                        bbat_chain = header_block[Header::SIZE..-1].unpack 'V*'
+                        mbat_block = @header.mbat_start
+                        @header.num_mbat.times do
+                                blocks = @bbat.read([mbat_block]).unpack 'V*'
+                                mbat_block = blocks.pop
+                                bbat_chain += blocks
+                        end
+                        # am i using num_bat in the right way?
+                        @bbat.load @bbat.read(bbat_chain[0, @header.num_bat])
+        
+                        # get block chain for directories, read it, then split it into chunks and load the
+                        # directory entries. semantics changed - used to cut at first dir where dir.type == 0
+                        @dirents = @bbat.read(@header.dirent_start).to_enum(:each_chunk, Dirent::SIZE).
+                                map { |str| Dirent.new self, str }.reject { |d| d.type_id == 0 }
+
+                        # now reorder from flat into a tree
+                        # links are stored in some kind of balanced binary tree
+                        # check that everything is visited at least, and at most once
+                        # similarly with the blocks of the file.
+                        # was thinking of moving this to Dirent.to_tree instead.
+                        class &lt;&lt; @dirents
+                                def to_tree idx=0
+                                        return [] if idx == Dirent::EOT
+                                        d = self[idx]
+                                        d.children = to_tree d.child
+                                        raise FormatError, &quot;directory #{d.inspect} used twice&quot; if d.idx
+                                        d.idx = idx
+                                        to_tree(d.prev) + [d] + to_tree(d.next)
+                                end
+                        end
+
+                        @root = @dirents.to_tree.first
+                        # silence this warning by default, its not really important (issue #5).
+                        # fairly common one appears to be &quot;R&quot; (from office OS X?) which smells
+                        # like some kind of UTF16 snafu, but scottwillson also has had some kanji...
+                        #Log.warn &quot;root name was #{@root.name.inspect}&quot; unless @root.name == 'Root Entry'
+                        unused = @dirents.reject(&amp;:idx).length
+                        Log.warn &quot;#{unused} unused directories&quot; if unused &gt; 0
+
+                        # FIXME i don't currently use @header.num_sbat which i should
+                        # hmm. nor do i write it. it means what exactly again?
+                        # which mode to use here?
+                        @sb_file = RangesIOResizeable.new @bbat, :first_block =&gt; @root.first_block, :size =&gt; @root.size
+                        @sbat = AllocationTable::Small.new self
+                        @sbat.load @bbat.read(@header.sbat_start)
+                end
+
+                def close
+                        @sb_file.close
+                        flush if @writeable
+                        @io.close if @close_parent
+                end
+
+                # the flush method is the main &quot;save&quot; method. all file contents are always
+                # written directly to the file by the RangesIO objects, all this method does
+                # is write out all the file meta data - dirents, allocation tables, file header
+                # etc.
+                #
+                # maybe add an option to zero the padding, and any remaining avail blocks in the
+                # allocation table.
+                #
+                # TODO: long and overly complex. simplify and test better. eg, perhaps move serialization
+                # of bbat to AllocationTable::Big. 
+                def flush
+                        # update root dirent, and flatten dirent tree
+                        @root.name = 'Root Entry'
+                        @root.first_block = @sb_file.first_block
+                        @root.size = @sb_file.size
+                        @dirents = @root.flatten
+
+                        # serialize the dirents using the bbat
+                        RangesIOResizeable.open @bbat, 'w', :first_block =&gt; @header.dirent_start do |io|
+                                io.write @dirents.map { |dirent| dirent.to_s }.join
+                                padding = (io.size / @bbat.block_size.to_f).ceil * @bbat.block_size - io.size
+                                io.write 0.chr * padding
+                                @header.dirent_start = io.first_block
+                        end
+
+                        # serialize the sbat
+                        # perhaps the blocks used by the sbat should be marked with BAT?
+                        RangesIOResizeable.open @bbat, 'w', :first_block =&gt; @header.sbat_start do |io|
+                                io.write @sbat.to_s
+                                @header.sbat_start = io.first_block
+                                @header.num_sbat = @bbat.chain(@header.sbat_start).length
+                        end
+
+                        # create RangesIOResizeable hooked up to the bbat. use that to claim bbat blocks using
+                        # truncate. then when its time to write, convert that chain and some chunk of blocks at
+                        # the end, into META_BAT blocks. write out the chain, and those meta bat blocks, and its
+                        # done.
+                        # this is perhaps not good, as we reclaim all bat blocks here, which
+                        # may include the sbat we just wrote. FIXME
+                        @bbat.map! do |b|
+                                b == AllocationTable::BAT || b == AllocationTable::META_BAT ? AllocationTable::AVAIL : b
+                        end
+
+                        # currently we use a loop. this could be better, but basically,
+                        # the act of writing out the bat, itself requires blocks which get
+                        # recorded in the bat.
+                        #
+                        # i'm sure that there'd be some simpler closed form solution to this. solve
+                        # recursive func:
+                        #
+                        #   num_mbat_blocks = ceil(max((mbat_len - 109) * 4 / block_size, 0))
+                        #   bbat_len = initial_bbat_len + num_mbat_blocks
+                        #   mbat_len = ceil(bbat_len * 4 / block_size)
+                        #
+                        # the actual bbat allocation table is itself stored throughout the file, and that chain
+                        # is stored in the initial blocks, and the mbat blocks.
+                        num_mbat_blocks = 0
+                        io = RangesIOResizeable.new @bbat, 'w', :first_block =&gt; AllocationTable::EOC
+                        # truncate now, so that we can simplify size calcs - the mbat blocks will be appended in a
+                        # contiguous chunk at the end.
+                        # hmmm, i think this truncate should be matched with a truncate of the underlying io. if you
+                        # delete a lot of stuff, and free up trailing blocks, the file size never shrinks. this can
+                        # be fixed easily, add an io truncate
+                        @bbat.truncate!
+                        before = @io.size
+                        @io.truncate @bbat.block_size * (@bbat.length + 1)
+                        while true
+                                # get total bbat size. equivalent to @bbat.to_s.length, but for the factoring in of
+                                # the mbat blocks. we can't just add the mbat blocks directly to the bbat, as as this iteration
+                                # progresses, more blocks may be needed for the bat itself (if there are no more gaps), and the
+                                # mbat must remain contiguous.
+                                bbat_data_len = ((@bbat.length + num_mbat_blocks) * 4 / @bbat.block_size.to_f).ceil * @bbat.block_size
+                                # now storing the excess mbat blocks also increases the size of the bbat:
+                                new_num_mbat_blocks = ([bbat_data_len / @bbat.block_size - 109, 0].max * 4 / (@bbat.block_size.to_f - 4)).ceil
+                                if new_num_mbat_blocks != num_mbat_blocks
+                                        # need more space for the mbat.
+                                        num_mbat_blocks = new_num_mbat_blocks
+                                elsif io.size != bbat_data_len
+                                        # need more space for the bat
+                                        # this may grow the bbat, depending on existing available blocks
+                                        io.truncate bbat_data_len
+                                else
+                                        break
+                                end
+                        end
+
+                        # now extract the info we want:
+                        ranges = io.ranges
+                        bbat_chain = @bbat.chain io.first_block
+                        io.close
+                        bbat_chain.each { |b| @bbat[b] = AllocationTable::BAT }
+                        # tack on the mbat stuff
+                        @header.num_bat = bbat_chain.length
+                        mbat_blocks = (0...num_mbat_blocks).map do
+                                block = @bbat.free_block
+                                @bbat[block] = AllocationTable::META_BAT
+                                block
+                        end
+                        @header.mbat_start = mbat_blocks.first || AllocationTable::EOC
+
+                        # now finally write the bbat, using a not resizable io.
+                        # the mode here will be 'r', which allows write atm. 
+                        RangesIO.open(@io, :ranges =&gt; ranges) { |f| f.write @bbat.to_s }
+
+                        # this is the mbat. pad it out.
+                        bbat_chain += [AllocationTable::AVAIL] * [109 - bbat_chain.length, 0].max
+                        @header.num_mbat = num_mbat_blocks
+                        if num_mbat_blocks != 0
+                                # write out the mbat blocks now. first of all, where are they going to be?
+                                mbat_data = bbat_chain[109..-1]
+                                # expand the mbat_data to include the linked list forward pointers.
+                                mbat_data = mbat_data.to_enum(:each_slice, @bbat.block_size / 4 - 1).to_a.
+                                        zip(mbat_blocks[1..-1] + [nil]).map { |a, b| b ? a + [b] : a }
+                                # pad out the last one.
+                                mbat_data.last.push(*([AllocationTable::AVAIL] * (@bbat.block_size / 4 - mbat_data.last.length)))
+                                RangesIO.open @io, :ranges =&gt; @bbat.ranges(mbat_blocks) do |f|
+                                        f.write mbat_data.flatten.pack('V*')
+                                end
+                        end
+
+                        # now seek back and write the header out
+                        @io.seek 0
+                        @io.write @header.to_s + bbat_chain[0, 109].pack('V*')
+                        @io.flush
+                end
+
+                def clear
+                        # initialize to equivalent of loading an empty ole document.
+                        Log.warn 'creating new ole storage object on non-writable io' unless @writeable
+                        @header = Header.new
+                        @bbat = AllocationTable::Big.new self
+                        @root = Dirent.new self, :type =&gt; :root, :name =&gt; 'Root Entry'
+                        @dirents = [@root]
+                        @root.idx = 0
+                        @sb_file.close if @sb_file
+                        @sb_file = RangesIOResizeable.new @bbat, :first_block =&gt; AllocationTable::EOC
+                        @sbat = AllocationTable::Small.new self
+                        # throw everything else the hell away
+                        @io.truncate 0
+                end
+
+                # could be useful with mis-behaving ole documents. or to just clean them up.
+                def repack temp=:file
+                        case temp
+                        when :file
+                                Tempfile.open 'ole-repack' do |io|
+                                        io.binmode
+                                        repack_using_io io
+                                end
+                        when :mem;  StringIO.open('', &amp;method(:repack_using_io))
+                        else raise ArgumentError, &quot;unknown temp backing #{temp.inspect}&quot;
+                        end
+                end
+
+                def repack_using_io temp_io
+                        @io.rewind
+                        IO.copy @io, temp_io
+                        clear
+                        Storage.open temp_io, nil, @params do |temp_ole|
+                                #temp_ole.root.type = :dir
+                                Dirent.copy temp_ole.root, root
+                        end
+                end
+
+                def bat_for_size size
+                        # note &gt;=, not &gt; previously.
+                        size &gt;= @header.threshold ? @bbat : @sbat
+                end
+
+                def inspect
+                        &quot;#&lt;#{self.class} io=#{@io.inspect} root=#{@root.inspect}&gt;&quot;
+                end
+
+                #
+                # A class which wraps the ole header
+                #
+                # Header.new can be both used to load from a string, or to create from
+                # defaults. Serialization is accomplished with the #to_s method.
+                #
+                class Header &lt; Struct.new(
+                                :magic, :clsid, :minor_ver, :major_ver, :byte_order, :b_shift, :s_shift,
+                                :reserved, :csectdir, :num_bat, :dirent_start, :transacting_signature, :threshold,
+                                :sbat_start, :num_sbat, :mbat_start, :num_mbat
+                        )
+                        PACK = 'a8 a16 v2 a2 v2 a6 V3 a4 V5'
+                        SIZE = 0x4c
+                        # i have seen it pointed out that the first 4 bytes of hex,
+                        # 0xd0cf11e0, is supposed to spell out docfile. hmmm :)
+                        MAGIC = &quot;\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1&quot;  # expected value of Header#magic
+                        # what you get if creating new header from scratch.
+                        # AllocationTable::EOC isn't available yet. meh.
+                        EOC = 0xfffffffe
+                        DEFAULT = [
+                                MAGIC, 0.chr * 16, 59, 3, &quot;\xfe\xff&quot;, 9, 6,
+                                0.chr * 6, 0, 1, EOC, 0.chr * 4,
+                                4096, EOC, 0, EOC, 0
+                        ]
+
+                        def initialize values=DEFAULT
+                                values = values.unpack(PACK) if String === values
+                                super(*values)
+                                validate!
+                        end
+
+                        def to_s
+                                to_a.pack PACK
+                        end
+
+                        def validate!
+                                raise FormatError, &quot;OLE2 signature is invalid&quot; unless magic == MAGIC
+                                if num_bat == 0 or # is that valid for a completely empty file?
+                                         # not sure about this one. basically to do max possible bat given size of mbat
+                                         num_bat &gt; 109 &amp;&amp; num_bat &gt; 109 + num_mbat * (1 &lt;&lt; b_shift - 2) or
+                                         # shouldn't need to use the mbat as there is enough space in the header block
+                                         num_bat &lt; 109 &amp;&amp; num_mbat != 0 or
+                                         # given the size of the header is 76, if b_shift &lt;= 6, blocks address the header.
+                                         s_shift &gt; b_shift or b_shift &lt;= 6 or b_shift &gt;= 31 or
+                                         # we only handle little endian
+                                         byte_order != &quot;\xfe\xff&quot;
+                                        raise FormatError, &quot;not valid OLE2 structured storage file&quot;
+                                end
+                                # relaxed this, due to test-msg/qwerty_[1-3]*.msg they all had
+                                # 3 for this value. 
+                                # transacting_signature != &quot;\x00&quot; * 4 or
+                                if threshold != 4096 or
+                                         num_mbat == 0 &amp;&amp; mbat_start != AllocationTable::EOC or
+                                         reserved != &quot;\x00&quot; * 6
+                                        Log.warn &quot;may not be a valid OLE2 structured storage file&quot;
+                                end
+                                true
+                        end
+                end
+
+                #
+                # +AllocationTable+'s hold the chains corresponding to files. Given
+                # an initial index, &lt;tt&gt;AllocationTable#chain&lt;/tt&gt; follows the chain, returning
+                # the blocks that make up that file.
+                #
+                # There are 2 allocation tables, the bbat, and sbat, for big and small
+                # blocks respectively. The block chain should be loaded using either
+                # &lt;tt&gt;Storage#read_big_blocks&lt;/tt&gt; or &lt;tt&gt;Storage#read_small_blocks&lt;/tt&gt;
+                # as appropriate.
+                #
+                # Whether or not big or small blocks are used for a file depends on
+                # whether its size is over the &lt;tt&gt;Header#threshold&lt;/tt&gt; level.
+                #
+                # An &lt;tt&gt;Ole::Storage&lt;/tt&gt; document is serialized as a series of directory objects,
+                # which are stored in blocks throughout the file. The blocks are either
+                # big or small, and are accessed using the &lt;tt&gt;AllocationTable&lt;/tt&gt;.
+                #
+                # The bbat allocation table's data is stored in the spare room in the header
+                # block, and in extra blocks throughout the file as referenced by the meta
+                # bat.  That chain is linear, as there is no higher level table.
+                #
+                # AllocationTable.new is used to create an empty table. It can parse a string
+                # with the #load method. Serialization is accomplished with the #to_s method.
+                #
+                class AllocationTable &lt; Array
+                        # a free block (I don't currently leave any blocks free), although I do pad out
+                        # the allocation table with AVAIL to the block size.
+                        AVAIL                 = 0xffffffff
+                        EOC                         = 0xfffffffe # end of a chain
+                        # these blocks are used for storing the allocation table chains
+                        BAT                         = 0xfffffffd
+                        META_BAT = 0xfffffffc
+
+                        attr_reader :ole, :io, :block_size
+                        def initialize ole
+                                @ole = ole
+                                @sparse = true
+                                super()
+                        end
+
+                        def load data
+                                replace data.unpack('V*')
+                        end
+
+                        def truncate
+                                # this strips trailing AVAILs. come to think of it, this has the potential to break
+                                # bogus ole. if you terminate using AVAIL instead of EOC, like I did before. but that is
+                                # very broken. however, if a chain ends with AVAIL, it should probably be fixed to EOC
+                                # at load time.
+                                temp = reverse
+                                not_avail = temp.find { |b| b != AVAIL } and temp = temp[temp.index(not_avail)..-1]
+                                temp.reverse
+                        end
+
+                        def truncate!
+                                replace truncate
+                        end
+
+                        def to_s
+                                table = truncate
+                                # pad it out some
+                                num = @ole.bbat.block_size / 4
+                                # do you really use AVAIL? they probably extend past end of file, and may shortly
+                                # be used for the bat. not really good.
+                                table += [AVAIL] * (num - (table.length % num)) if (table.length % num) != 0
+                                table.pack 'V*'
+                        end
+
+                        # rewrote this to be non-recursive as it broke on a large attachment
+                        # chain with a stack error
+                        def chain idx
+                                a = []
+                                until idx &gt;= META_BAT
+                                        raise FormatError, &quot;broken allocationtable chain&quot; if idx &lt; 0 || idx &gt; length
+                                        a &lt;&lt; idx
+                                        idx = self[idx]
+                                end
+                                Log.warn &quot;invalid chain terminator #{idx}&quot; unless idx == EOC
+                                a
+                        end
+                        
+                        # Turn a chain (an array given by +chain+) of blocks (optionally
+                        # truncated to +size+) into an array of arrays describing the stretches of
+                        # bytes in the file that it belongs to.
+                        #
+                        # The blocks are Big or Small blocks depending on the table type.
+                        def blocks_to_ranges chain, size=nil
+                                # truncate the chain if required
+                                chain = chain[0, (size.to_f / block_size).ceil] if size
+                                # convert chain to ranges of the block size
+                                ranges = chain.map { |i| [block_size * i, block_size] }
+                                # truncate final range if required
+                                ranges.last[1] -= (ranges.length * block_size - size) if ranges.last and size
+                                ranges
+                        end
+
+                        def ranges chain, size=nil
+                                chain = self.chain(chain) unless Array === chain
+                                blocks_to_ranges chain, size
+                        end
+
+                        # quick shortcut. chain can be either a head (in which case the table is used to
+                        # turn it into a chain), or a chain. it is converted to ranges, then to rangesio.
+                        def open chain, size=nil, &amp;block
+                                RangesIO.open @io, :ranges =&gt; ranges(chain, size), &amp;block
+                        end
+
+                        def read chain, size=nil
+                                open chain, size, &amp;:read
+                        end
+
+                        # catch any method that may add an AVAIL somewhere in the middle, thus invalidating
+                        # the @sparse speedup for free_block. annoying using eval, but define_method won't
+                        # work for this.
+                        # FIXME
+                        [:map!, :collect!].each do |name|
+                                eval &lt;&lt;-END
+                                        def #{name}(*args, &amp;block)
+                                                @sparse = true
+                                                super
+                                        end
+                                END
+                        end
+
+                        def []= idx, val
+                                @sparse = true if val == AVAIL
+                                super
+                        end
+
+                        def free_block
+                                if @sparse
+                                        i = index(AVAIL) and return i
+                                        @sparse = false
+                                end
+                                push AVAIL
+                                length - 1
+                        end
+
+                        # must return first_block. modifies +blocks+ in place
+                        def resize_chain blocks, size
+                                new_num_blocks = (size / block_size.to_f).ceil
+                                old_num_blocks = blocks.length
+                                if new_num_blocks &lt; old_num_blocks
+                                        # de-allocate some of our old blocks. TODO maybe zero them out in the file???
+                                        (new_num_blocks...old_num_blocks).each { |i| self[blocks[i]] = AVAIL }
+                                        self[blocks[new_num_blocks-1]] = EOC if new_num_blocks &gt; 0
+                                        blocks.slice! new_num_blocks..-1
+                                elsif new_num_blocks &gt; old_num_blocks
+                                        # need some more blocks.
+                                        last_block = blocks.last
+                                        (new_num_blocks - old_num_blocks).times do
+                                                block = free_block
+                                                # connect the chain. handle corner case of blocks being [] initially
+                                                self[last_block] = block if last_block
+                                                blocks &lt;&lt; block
+                                                last_block = block
+                                                self[last_block] = EOC
+                                        end
+                                end
+                                # update ranges, and return that also now
+                                blocks
+                        end
+
+                        class Big &lt; AllocationTable
+                                def initialize(*args)
+                                        super
+                                        @block_size = 1 &lt;&lt; @ole.header.b_shift
+                                        @io = @ole.io
+                                end
+
+                                # Big blocks are kind of -1 based, in order to not clash with the header.
+                                def blocks_to_ranges chain, size=nil
+                                        #super chain.map { |b| b + 1 }, size
+                                        # duplicated from AllocationTable#blocks_to_ranges to avoid chain.map
+                                        # which was decent part of benchmark profile
+                                        chain = chain[0, (size.to_f / block_size).ceil] if size
+                                        ranges = chain.map { |i| [block_size * (i + 1), block_size] }
+                                        ranges.last[1] -= (ranges.length * block_size - size) if ranges.last and size
+                                        ranges
+                                end
+                        end
+
+                        class Small &lt; AllocationTable
+                                def initialize(*args)
+                                        super
+                                        @block_size = 1 &lt;&lt; @ole.header.s_shift
+                                        @io = @ole.sb_file
+                                end
+                        end
+                end
+
+                # like normal RangesIO, but Ole::Storage specific. the ranges are backed by an
+                # AllocationTable, and can be resized. used for read/write to 2 streams:
+                # 1. serialized dirent data
+                # 2. sbat table data
+                # 3. all dirents but through RangesIOMigrateable below
+                #
+                # Note that all internal access to first_block is through accessors, as it is sometimes
+                # useful to redirect it.
+                class RangesIOResizeable &lt; RangesIO
+                        attr_reader   :bat
+                        attr_accessor :first_block
+                        def initialize bat, mode='r', params={}
+                                mode, params = 'r', mode if Hash === mode
+                                first_block, size = params.values_at :first_block, :size
+                                raise ArgumentError, 'must specify first_block' unless first_block
+                                @bat = bat
+                                self.first_block = first_block
+                                # we now cache the blocks chain, for faster resizing.
+                                @blocks = @bat.chain first_block
+                                super @bat.io, mode, :ranges =&gt; @bat.ranges(@blocks, size)
+                        end
+
+                        def truncate size
+                                # note that old_blocks is != @ranges.length necessarily. i'm planning to write a
+                                # merge_ranges function that merges sequential ranges into one as an optimization.
+                                @bat.resize_chain @blocks, size
+                                @pos = size if @pos &gt; size
+                                self.ranges = @bat.ranges(@blocks, size)
+                                self.first_block = @blocks.empty? ? AllocationTable::EOC : @blocks.first
+
+                                # don't know if this is required, but we explicitly request our @io to grow if necessary
+                                # we never shrink it though. maybe this belongs in allocationtable, where smarter decisions
+                                # can be made.
+                                # maybe its ok to just seek out there later??
+                                max = @ranges.map { |pos, len| pos + len }.max || 0
+                                @io.truncate max if max &gt; @io.size
+                        end
+                end
+
+                # like RangesIOResizeable, but Ole::Storage::Dirent specific. provides for migration
+                # between bats based on size, and updating the dirent.
+                class RangesIOMigrateable &lt; RangesIOResizeable
+                        attr_reader :dirent
+                        def initialize dirent, mode='r'
+                                @dirent = dirent
+                                super @dirent.ole.bat_for_size(@dirent.size), mode,
+                                        :first_block =&gt; @dirent.first_block, :size =&gt; @dirent.size
+                        end
+
+                        def truncate size
+                                bat = @dirent.ole.bat_for_size size
+                                if bat.class != @bat.class
+                                        # bat migration needed! we need to backup some data. the amount of data
+                                        # should be &lt;= @ole.header.threshold, so we can just hold it all in one buffer.
+                                        # backup this
+                                        pos = [@pos, size].min
+                                        self.pos = 0
+                                        keep = read [@size, size].min
+                                        # this does a normal truncate to 0, removing our presence from the old bat, and
+                                        # rewrite the dirent's first_block
+                                        super 0
+                                        @bat = bat
+                                        # just change the underlying io from right under everyone :)
+                                        @io = bat.io
+                                        # important to do this now, before the write. as the below write will always
+                                        # migrate us back to sbat! this will now allocate us +size+ in the new bat.
+                                        super
+                                        self.pos = 0
+                                        write keep
+                                        self.pos = pos
+                                else
+                                        super
+                                end
+                                # now just update the file
+                                @dirent.size = size
+                        end
+
+                        # forward this to the dirent
+                        def first_block
+                                @dirent.first_block
+                        end
+
+                        def first_block= val
+                                @dirent.first_block = val
+                        end
+                end
+
+                #
+                # A class which wraps an ole directory entry. Can be either a directory
+                # (&lt;tt&gt;Dirent#dir?&lt;/tt&gt;) or a file (&lt;tt&gt;Dirent#file?&lt;/tt&gt;)
+                #
+                # Most interaction with &lt;tt&gt;Ole::Storage&lt;/tt&gt; is through this class.
+                # The 2 most important functions are &lt;tt&gt;Dirent#children&lt;/tt&gt;, and
+                # &lt;tt&gt;Dirent#data&lt;/tt&gt;.
+                # 
+                # was considering separate classes for dirs and files. some methods/attrs only
+                # applicable to one or the other.
+                #
+                # As with the other classes, #to_s performs the serialization.
+                #
+                class Dirent &lt; Struct.new(
+                                :name_utf16, :name_len, :type_id, :colour, :prev, :next, :child,
+                                :clsid, :flags, # dirs only
+                                :create_time_str, :modify_time_str, # files only
+                                :first_block, :size, :reserved
+                        )
+                        include RecursivelyEnumerable
+
+                        PACK = 'a64 v C C V3 a16 V a8 a8 V2 a4'
+                        SIZE = 128
+                        TYPE_MAP = {
+                                # this is temporary
+                                0 =&gt; :empty,
+                                1 =&gt; :dir,
+                                2 =&gt; :file,
+                                5 =&gt; :root
+                        }
+                        # something to do with the fact that the tree is supposed to be red-black
+                        COLOUR_MAP = {
+                                0 =&gt; :red,
+                                1 =&gt; :black
+                        }
+                        # used in the next / prev / child stuff to show that the tree ends here.
+                        # also used for first_block for directory.
+                        EOT = 0xffffffff
+                        DEFAULT = [
+                                0.chr * 2, 2, 0, # will get overwritten
+                                1, EOT, EOT, EOT,
+                                0.chr * 16, 0, nil, nil,
+                                AllocationTable::EOC, 0, 0.chr * 4
+                        ]
+
+                        # This returns all the children of this +Dirent+. It is filled in
+                        # when the tree structure is recreated.
+                        attr_reader :children
+                        attr_reader :name
+                        attr_reader :ole, :type, :create_time, :modify_time
+                        attr_reader :parent
+
+                        # i think its just used by the tree building
+                        attr_accessor :idx
+
+                        # these are for internal use and are used for faster lookup.
+                        attr_reader :name_lookup
+                        attr_writer :parent
+                        protected :name_lookup, :parent=
+
+                        def initialize ole, values=DEFAULT, params={}
+                                @ole = ole                                
+                                values, params = DEFAULT, values if Hash === values
+                                values = values.unpack(PACK) if String === values
+                                super(*values)
+
+                                # extra parsing from the actual struct values
+                                @name = params[:name] || Types::Variant.load(Types::VT_LPWSTR, name_utf16[0...name_len])
+                                @type = if params[:type]
+                                        unless TYPE_MAP.values.include?(params[:type])
+                                                raise ArgumentError, &quot;unknown type #{params[:type].inspect}&quot;
+                                        end
+                                        params[:type]
+                                else
+                                        TYPE_MAP[type_id] or raise FormatError, &quot;unknown type_id #{type_id.inspect}&quot;
+                                end
+
+                                # further extra type specific stuff
+                                if file?
+                                        default_time = @ole.params[:update_timestamps] ? Types::FileTime.now : nil
+                                        @create_time ||= default_time
+                                        @modify_time ||= default_time
+                                        @create_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if create_time_str
+                                        @modify_time = Types::Variant.load(Types::VT_FILETIME, create_time_str) if modify_time_str
+                                        @children = nil
+                                        @name_lookup = nil
+                                else
+                                        @create_time = nil
+                                        @modify_time = nil
+                                        self.size = 0 unless @type == :root
+                                        @children = []
+                                        @name_lookup = {}
+                                end
+
+                                @parent = nil
+                                
+                                # to silence warnings. used for tree building at load time
+                                # only.
+                                @idx = nil
+                        end
+
+                        def name= name
+                                if @parent
+                                        map = @parent.instance_variable_get :@name_lookup
+                                        map.delete @name
+                                        map[name] = self
+                                end
+                                @name = name
+                        end
+
+                        def children= children
+                                @children = []
+                                children.each { |child| self &lt;&lt; child }
+                        end
+
+                        def open mode='r'
+                                raise Errno::EISDIR unless file?
+                                io = RangesIOMigrateable.new self, mode
+                                @modify_time = Types::FileTime.now if io.mode.writeable?
+                                if block_given?
+                                        begin   yield io
+                                        ensure; io.close
+                                        end
+                                else io
+                                end
+                        end
+
+                        def read limit=nil
+                                open { |io| io.read limit }
+                        end
+
+                        def file?
+                                type == :file
+                        end
+
+                        def dir?
+                                # to count root as a dir.
+                                !file?
+                        end
+
+                        # maybe need some options regarding case sensitivity.
+                        def / name
+                                @name_lookup[name]
+                        end
+
+                        def [] idx
+                                if String === idx
+                                        #warn 'String form of Dirent#[] is deprecated'
+                                        self / idx
+                                else
+                                        super
+                                end
+                        end
+
+                        # move to ruby-msg. and remove from here
+                        def time
+                                #warn 'Dirent#time is deprecated'
+                                create_time || modify_time
+                        end
+
+                        def each_child(&amp;block)
+                                @children.each(&amp;block)
+                        end
+
+                        # flattens the tree starting from here into +dirents+. note it modifies its argument.
+                        def flatten dirents=[]
+                                @idx = dirents.length
+                                dirents &lt;&lt; self
+                                if file?
+                                        self.prev = self.next = self.child = EOT
+                                else
+                                        children.each { |child| child.flatten dirents } 
+                                        self.child = Dirent.flatten_helper children
+                                end
+                                dirents
+                        end
+
+                        # i think making the tree structure optimized is actually more complex than this, and
+                        # requires some intelligent ordering of the children based on names, but as long as
+                        # it is valid its ok.
+                        # actually, i think its ok. gsf for example only outputs a singly-linked-list, where
+                        # prev is always EOT.
+                        def self.flatten_helper children
+                                return EOT if children.empty?
+                                i = children.length / 2
+                                this = children[i]
+                                this.prev, this.next = [(0...i), (i+1..-1)].map { |r| flatten_helper children[r] }
+                                this.idx
+                        end
+
+                        def to_s
+                                tmp = Types::Variant.dump(Types::VT_LPWSTR, name)
+                                tmp = tmp[0, 62] if tmp.length &gt; 62
+                                tmp += 0.chr * 2
+                                self.name_len = tmp.length
+                                self.name_utf16 = tmp + 0.chr * (64 - tmp.length)
+                                # type_id can perhaps be set in the initializer, as its read only now.
+                                self.type_id = TYPE_MAP.to_a.find { |id, name| @type == name }.first
+                                # for the case of files, it is assumed that that was handled already
+                                # note not dir?, so as not to override root's first_block
+                                self.first_block = Dirent::EOT if type == :dir
+                                if file?
+                                        # this is messed up. it changes the time stamps regardless of whether the file
+                                        # was actually touched. instead, any open call with a writeable mode, should update
+                                        # the modify time. create time would be set in new.
+                                        if @ole.params[:update_timestamps]
+                                                self.create_time_str = Types::Variant.dump Types::VT_FILETIME, @create_time
+                                                self.modify_time_str = Types::Variant.dump Types::VT_FILETIME, @modify_time
+                                        end
+                                else
+                                        self.create_time_str = 0.chr * 8
+                                        self.modify_time_str = 0.chr * 8
+                                end
+                                to_a.pack PACK
+                        end
+
+                        def inspect
+                                str = &quot;#&lt;Dirent:#{name.inspect}&quot;
+                                # perhaps i should remove the data snippet. its not that useful anymore.
+                                # there is also some dir specific stuff. like clsid, flags, that i should
+                                # probably include
+                                if file?
+                                        tmp = read 9
+                                        data = tmp.length == 9 ? tmp[0, 5] + '...' : tmp
+                                        str &lt;&lt; &quot; size=#{size}&quot; +
+                                                &quot;#{modify_time ? ' modify_time=' + modify_time.to_s.inspect : nil}&quot; +
+                                                &quot; data=#{data.inspect}&quot;
+                                end
+                                str + '&gt;'
+                        end
+
+                        def &lt;&lt; child
+                                child.parent = self
+                                @name_lookup[child.name] = child
+                                @children &lt;&lt; child
+                        end
+
+                        # remove the Dirent +child+ from the children array, truncating the data
+                        # by default.
+                        def delete child, truncate=true
+                                # remove from our child array, so that on reflatten and re-creation of @dirents, it will be gone
+                                unless @children.delete(child)
+                                        raise ArgumentError, &quot;#{child.inspect} not a child of #{self.inspect}&quot;
+                                end
+                                @name_lookup.delete(child.name)
+                                child.parent = nil
+                                # free our blocks
+                                child.open { |io| io.truncate 0 } if child.file?
+                        end
+
+                        def self.copy src, dst
+                                # copies the contents of src to dst. must be the same type. this will throw an
+                                # error on copying to root. maybe this will recurse too much for big documents??
+                                raise ArgumentError, 'differing types' if src.file? and !dst.file?
+                                dst.name = src.name
+                                if src.dir?
+                                        src.children.each do |src_child|
+                                                dst_child = Dirent.new dst.ole, :type =&gt; src_child.type
+                                                dst &lt;&lt; dst_child
+                                                Dirent.copy src_child, dst_child
+                                        end
+                                else
+                                        src.open do |src_io|
+                                                dst.open { |dst_io| IO.copy src_io, dst_io }
+                                        end
+                                end
+                        end
+                end
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/base.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragefile_systemrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/file_system.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/file_system.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/file_system.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,394 @@
</span><ins>+#
+# = Introduction
+#
+# This file intends to provide file system-like api support, a la &lt;tt&gt;zip/zipfilesystem&lt;/tt&gt;.
+#
+# = TODO
+# 
+# - need to implement some more IO functions on RangesIO, like #puts, #print
+#   etc, like AbstractOutputStream from zipfile.
+#
+# - check Dir.mkdir, and File.open, and File.rename, to add in filename 
+#   length checks (max 32 / 31 or something).
+#   do the automatic truncation, and add in any necessary warnings.
+#
+# - File.split('a/') == File.split('a') == ['.', 'a']
+#   the implication of this, is that things that try to force directory
+#   don't work. like, File.rename('a', 'b'), should work if a is a file
+#   or directory, but File.rename('a/', 'b') should only work if a is
+#   a directory. tricky, need to clean things up a bit more.
+#   i think a general path name =&gt; dirent method would work, with flags
+#   about what should raise an error. 
+#
+# - Need to look at streamlining things after getting all the tests passing,
+#   as this file's getting pretty long - almost half the real implementation.
+#   and is probably more inefficient than necessary.
+#   too many exceptions in the expected path of certain functions.
+#
+# - should look at profiles before and after switching ruby-msg to use
+#   the filesystem api.
+#
+
+require 'ole/storage'
+
+module Ole # :nodoc:
+        class Storage
+                def file
+                        @file ||= FileClass.new self
+                end
+
+                def dir
+                        @dir ||= DirClass.new self
+                end
+
+                # tries to get a dirent for path. return nil if it doesn't exist
+                # (change it)
+                def dirent_from_path path
+                        dirent = @root
+                        path = file.expand_path(path).split('/')
+                        until path.empty?
+                                part = path.shift
+                                next if part.empty?
+                                return nil if dirent.file?
+                                return nil unless dirent = dirent/part
+                        end                        
+                        dirent
+                end
+
+                class FileClass
+                        class Stat
+                                attr_reader :ftype, :size, :blocks, :blksize
+                                attr_reader :nlink, :uid, :gid, :dev, :rdev, :ino
+                                def initialize dirent
+                                        @dirent = dirent
+                                        @size = dirent.size
+                                        if file?
+                                                @ftype = 'file'
+                                                bat = dirent.ole.bat_for_size(dirent.size)
+                                                @blocks = bat.chain(dirent.first_block).length
+                                                @blksize = bat.block_size
+                                        else
+                                                @ftype = 'directory'
+                                                @blocks = 0
+                                                @blksize = 0
+                                        end
+                                        # a lot of these are bogus. ole file format has no analogs
+                                        @nlink = 1
+                                        @uid, @gid = 0, 0
+                                        @dev, @rdev = 0, 0
+                                        @ino = 0
+                                        # need to add times - atime, mtime, ctime. 
+                                end
+
+                                alias rdev_major :rdev
+                                alias rdev_minor :rdev
+
+                                def file?
+                                        @dirent.file?
+                                end
+
+                                def directory?
+                                        @dirent.dir?
+                                end
+                                
+                                def size?
+                                        size if file?
+                                end
+
+                                def inspect
+                                        pairs = (instance_variables - ['@dirent']).map do |n|
+                                                &quot;#{n[1..-1]}=#{instance_variable_get n}&quot;
+                                        end
+                                        &quot;#&lt;#{self.class} #{pairs * ', '}&gt;&quot;
+                                end
+                        end
+
+                        def initialize ole
+                                @ole = ole
+                        end
+
+                        def expand_path path
+                                # its already absolute if it starts with a '/'
+                                unless path =~ /^\//
+                                        # get the raw stored pwd value (its blank for root)
+                                        pwd = @ole.dir.instance_variable_get :@pwd
+                                        path = &quot;#{pwd}/#{path}&quot;
+                                end
+                                # at this point its already absolute. we use File.expand_path
+                                # just for the .. and . handling
+                                # No longer use RUBY_PLATFORM =~ /win/ as it matches darwin. better way?
+                                if File::ALT_SEPARATOR != &quot;\\&quot;
+                                        File.expand_path(path)
+                                else
+                                        File.expand_path(path)[2..-1]
+                                end
+                        end
+
+                        # +orig_path+ is just so that we can use the requested path
+                        # in the error messages even if it has been already modified
+                        def dirent_from_path path, orig_path=nil
+                                orig_path ||= path
+                                dirent = @ole.dirent_from_path path
+                                raise Errno::ENOENT, orig_path unless dirent
+                                raise Errno::EISDIR, orig_path if dirent.dir?
+                                dirent
+                        end
+                        private :dirent_from_path
+
+                        def exists? path
+                                !!@ole.dirent_from_path(path)
+                        end
+                        alias exist? :exists?
+
+                        def file? path
+                                dirent = @ole.dirent_from_path path
+                                dirent and dirent.file?
+                        end
+
+                        def directory? path
+                                dirent = @ole.dirent_from_path path
+                                dirent and dirent.dir?
+                        end
+
+                        def open path, mode='r', &amp;block
+                                if IO::Mode.new(mode).create?
+                                        begin
+                                                dirent = dirent_from_path path
+                                        rescue Errno::ENOENT
+                                                # maybe instead of repeating this everywhere, i should have
+                                                # a get_parent_dirent function.
+                                                parent_path, basename = File.split expand_path(path)
+                                                parent = @ole.dir.send :dirent_from_path, parent_path, path
+                                                parent &lt;&lt; dirent = Dirent.new(@ole, :type =&gt; :file, :name =&gt; basename)
+                                        end
+                                else
+                                        dirent = dirent_from_path path
+                                end
+                                dirent.open mode, &amp;block
+                        end
+
+                        # explicit wrapper instead of alias to inhibit block
+                        def new path, mode='r'
+                                open path, mode
+                        end
+
+                        def size path
+                                dirent_from_path(path).size
+                        rescue Errno::EISDIR
+                                # kind of arbitrary. I'm getting 4096 from ::File, but
+                                # the zip tests want 0.
+                                0
+                        end
+                        
+                        def size? path
+                                dirent_from_path(path).size
+                                # any other exceptions i need to rescue?
+                        rescue Errno::ENOENT, Errno::EISDIR
+                                nil
+                        end
+
+                        def stat path
+                                # we do this to allow dirs.
+                                dirent = @ole.dirent_from_path path
+                                raise Errno::ENOENT, path unless dirent
+                                Stat.new dirent
+                        end
+
+                        def read path
+                                open path, &amp;:read
+                        end
+
+                        # most of the work this function does is moving the dirent between
+                        # 2 parents. the actual name changing is quite simple.
+                        # File.rename can move a file into another folder, which is why i've
+                        # done it too, though i think its not always possible...
+                        #
+                        # FIXME File.rename can be used for directories too....
+                        def rename from_path, to_path
+                                # check what we want to rename from exists. do it this
+                                # way to allow directories.
+                                dirent = @ole.dirent_from_path from_path
+                                raise Errno::ENOENT, from_path unless dirent
+                                # delete what we want to rename to if necessary
+                                begin
+                                        unlink to_path
+                                rescue Errno::ENOENT
+                                        # we actually get here, but rcov doesn't think so. add 1 + 1 to
+                                        # keep rcov happy for now... :)
+                                        1 + 1
+                                end
+                                # reparent the dirent
+                                to_parent_path, to_basename = File.split expand_path(to_path)
+                                from_parent = dirent.parent
+                                to_parent = @ole.dir.send :dirent_from_path, to_parent_path, to_path
+                                from_parent.delete dirent, false
+                                # and also change its name
+                                dirent.name = to_basename
+                                to_parent &lt;&lt; dirent
+                                0
+                        end
+
+                        def unlink(*paths)
+                                paths.each do |path|
+                                        dirent = dirent_from_path path
+                                        dirent.parent.delete dirent
+                                end
+                                paths.length # hmmm. as per ::File ?
+                        end
+                        alias delete :unlink
+                end
+
+                #
+                # An *instance* of this class is supposed to provide similar methods
+                # to the class methods of Dir itself.
+                #
+                # Fairly complete - like zip/zipfilesystem's implementation, i provide
+                # everything except chroot and glob. glob could be done with a glob
+                # to regex conversion, and then simply match in the entries array...
+                # although recursive glob complicates that somewhat.
+                #
+                # Dir.chroot, Dir.glob, Dir.[], and Dir.tmpdir is the complete list of
+                # methods still missing.
+                #
+                class DirClass
+                        def initialize ole
+                                @ole = ole
+                                @pwd = ''
+                        end
+
+                        # +orig_path+ is just so that we can use the requested path
+                        # in the error messages even if it has been already modified
+                        def dirent_from_path path, orig_path=nil
+                                orig_path ||= path
+                                dirent = @ole.dirent_from_path path
+                                raise Errno::ENOENT,  orig_path unless dirent
+                                raise Errno::ENOTDIR, orig_path unless dirent.dir?
+                                dirent
+                        end
+                        private :dirent_from_path
+
+                        def open path
+                                dir = Dir.new path, entries(path)
+                                return dir unless block_given?
+                                yield dir
+                        end
+
+                        # as for file, explicit alias to inhibit block
+                        def new path
+                                open path
+                        end
+
+                        # pwd is always stored without the trailing slash. we handle
+                        # the root case here
+                        def pwd
+                                return '/' if @pwd.empty?
+                                @pwd
+                        end
+                        alias getwd :pwd
+
+                        def chdir orig_path
+                                # make path absolute, squeeze slashes, and remove trailing slash
+                                path = @ole.file.expand_path(orig_path).squeeze('/').sub(/\/$/, '')
+                                # this is just for the side effects of the exceptions if invalid
+                                dirent_from_path path, orig_path
+                                if block_given?
+                                        old_pwd = @pwd
+                                        begin
+                                                @pwd = path
+                                                yield
+                                        ensure
+                                                @pwd = old_pwd
+                                        end
+                                else
+                                        @pwd = path
+                                        0
+                                end
+                        end        
+
+                        def entries path
+                                dirent = dirent_from_path path
+                                # Not sure about adding on the dots...
+                                entries = %w[. ..] + dirent.children.map(&amp;:name)
+                                # do some checks about un-reachable files
+                                seen = {}
+                                entries.each do |n|
+                                        Log.warn &quot;inaccessible file (filename contains slash) - #{n.inspect}&quot; if n['/']
+                                        Log.warn &quot;inaccessible file (duplicate filename) - #{n.inspect}&quot; if seen[n]
+                                        seen[n] = true
+                                end
+                                entries
+                        end
+
+                        def foreach path, &amp;block
+                                entries(path).each(&amp;block)
+                        end
+
+                        def mkdir path
+                                parent_path, basename = File.split @ole.file.expand_path(path)
+                                # note that we will complain about the full path despite accessing
+                                # the parent path. this is consistent with ::Dir
+                                parent = dirent_from_path parent_path, path
+                                # now, we first should ensure that it doesn't already exist
+                                # either as a file or a directory.
+                                raise Errno::EEXIST, path if parent/basename
+                                parent &lt;&lt; Dirent.new(@ole, :type =&gt; :dir, :name =&gt; basename)
+                                0
+                        end
+
+                        def rmdir path
+                                dirent = dirent_from_path path
+                                raise Errno::ENOTEMPTY, path unless dirent.children.empty?
+                                dirent.parent.delete dirent
+                                0 # hmmm. as per ::Dir ?
+                        end
+                        alias delete :rmdir
+                        alias unlink :rmdir
+
+                        # note that there is nothing remotely ole specific about
+                        # this class. it simply provides the dir like sequential access
+                        # methods on top of an array.
+                        class Dir
+                                include Enumerable
+
+                                attr_reader :path
+                                def initialize path, entries
+                                        @path, @entries, @pos = path, entries, 0
+                                        @closed = false
+                                end
+                                
+                                def pos
+                                        raise IOError if @closed
+                                        @pos
+                                end
+
+                                def each(&amp;block)
+                                        raise IOError if @closed
+                                        @entries.each(&amp;block)
+                                end
+
+                                def close
+                                        @closed = true
+                                end
+
+                                def read
+                                        raise IOError if @closed
+                                        @entries[pos]
+                                ensure
+                                        @pos += 1 if pos &lt; @entries.length
+                                end
+
+                                def pos= pos
+                                        raise IOError if @closed
+                                        @pos = [[0, pos].max, @entries.length].min
+                                end
+                                alias tell :pos
+                                alias seek :pos=
+
+                                def rewind
+                                        seek 0
+                                end
+                        end
+                end
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragemeta_datarb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/meta_data.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/meta_data.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage/meta_data.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,148 @@
</span><ins>+require 'ole/types/property_set'
+
+module Ole
+        class Storage
+                #
+                # The MetaData class is designed to be high level interface to all the
+                # underlying meta data stored within different sections, themselves within
+                # different property set streams.
+                #
+                # With this class, you can simply get properties using their names, without
+                # needing to know about the underlying guids, property ids etc.
+                #
+                # Example:
+                #
+                #   Ole::Storage.open('test.doc') { |ole| p ole.meta_data.doc_author }
+                #
+                # TODO:
+                #
+                # * add write support
+                # * fix some of the missing type coercion (eg FileTime)
+                # * maybe add back the ability to access individual property sets as a unit
+                #   directly. ie &lt;tt&gt;ole.summary_information&lt;/tt&gt;. Is this useful?
+                # * full key support, for unknown keys, like
+                #   &lt;tt&gt;ole.meta_data[myguid, myid]&lt;/tt&gt;. probably needed for user-defined
+                #   properties too.
+                #
+                class MetaData
+                        include Enumerable
+
+                        FILE_MAP = {
+                                Types::PropertySet::FMTID_SummaryInformation =&gt; &quot;\005SummaryInformation&quot;,
+                                Types::PropertySet::FMTID_DocSummaryInfo =&gt; &quot;\005DocumentSummaryInformation&quot;
+                        }
+
+                        FORMAT_MAP = {
+                                'MSWordDoc' =&gt; :doc
+                        }
+
+                        CLSID_EXCEL97 = Types::Clsid.parse &quot;{00020820-0000-0000-c000-000000000046}&quot;
+                        CLSID_EXCEL95 = Types::Clsid.parse &quot;{00020810-0000-0000-c000-000000000046}&quot;
+                        CLSID_WORD97  = Types::Clsid.parse &quot;{00020906-0000-0000-c000-000000000046}&quot;
+                        CLSID_WORD95  = Types::Clsid.parse &quot;{00020900-0000-0000-c000-000000000046}&quot;
+
+                        CLSID_MAP = {
+                                CLSID_EXCEL97 =&gt; :xls,
+                                CLSID_EXCEL95 =&gt; :xls,
+                                CLSID_WORD97  =&gt; :doc,
+                                CLSID_WORD95  =&gt; :doc
+                        }
+
+                        MIME_TYPES = {
+                                :xls =&gt; 'application/vnd.ms-excel',
+                                :doc =&gt; 'application/msword',
+                                :ppt =&gt; 'application/vnd.ms-powerpoint',
+                                # not registered at IANA, but seems most common usage
+                                :msg =&gt; 'application/vnd.ms-outlook',
+                                # this is my default fallback option. also not registered at IANA.
+                                # file(1)'s default is application/msword, which is useless...
+                                nil  =&gt; 'application/x-ole-storage'
+                        }
+
+                        def initialize ole
+                                @ole = ole
+                        end
+
+                        # i'm thinking of making file_format and mime_type available through
+                        # #[], #each, and #to_h also, as calculated meta data (not assignable)
+
+                        def comp_obj
+                                return {} unless dirent = @ole.root[&quot;\001CompObj&quot;]
+                                data = dirent.read
+                                # see - https://gnunet.org/svn/Extractor/doc/StarWrite_File_Format.html
+                                # compobj_version: 0x0001
+                                # byte_order: 0xffe
+                                # windows_version: 0x00000a03 (win31 apparently)
+                                # marker: 0xffffffff
+                                compobj_version, byte_order, windows_version, marker, clsid =
+                                        data.unpack(&quot;vvVVa#{Types::Clsid::SIZE}&quot;)
+                                strings = []
+                                i = 28
+                                while i &lt; data.length
+                                        len = data[i, 4].unpack('V').first
+                                        i += 4
+                                        strings &lt;&lt; data[i, len - 1]
+                                        i += len
+                                end
+                                # in the unknown chunk, you usually see something like 'Word.Document.6'
+                                {:username =&gt; strings[0], :file_format =&gt; strings[1], :unknown =&gt; strings[2..-1]}
+                        end
+                        private :comp_obj
+
+                        def file_format
+                                comp_obj[:file_format]
+                        end
+
+                        def mime_type
+                                # based on the CompObj stream contents
+                                type = FORMAT_MAP[file_format]
+                                return MIME_TYPES[type] if type
+
+                                # based on the root clsid
+                                type = CLSID_MAP[Types::Clsid.load(@ole.root.clsid)]
+                                return MIME_TYPES[type] if type
+
+                                # fallback to heuristics
+                                has_file = Hash[*@ole.root.children.map { |d| [d.name.downcase, true] }.flatten]
+                                return MIME_TYPES[:msg] if has_file['__nameid_version1.0'] or has_file['__properties_version1.0']
+                                return MIME_TYPES[:doc] if has_file['worddocument'] or has_file['document']
+                                return MIME_TYPES[:xls] if has_file['workbook'] or has_file['book']
+
+                                MIME_TYPES[nil]
+                        end
+
+                        def [] key
+                                pair = Types::PropertySet::PROPERTY_MAP[key.to_s] or return nil
+                                file = FILE_MAP[pair.first] or return nil
+                                dirent = @ole.root[file] or return nil
+                                dirent.open { |io| return Types::PropertySet.new(io)[key] }
+                        end
+
+                        def []= key, value
+                                raise NotImplementedError, 'meta data writes not implemented'
+                        end
+
+                        def each(&amp;block)
+                                FILE_MAP.values.each do |file|
+                                        dirent = @ole.root[file] or next
+                                        dirent.open { |io| Types::PropertySet.new(io).each(&amp;block) }
+                                end
+                        end
+
+                        def to_h
+                                inject({}) { |hash, (name, value)| hash.update name.to_sym =&gt; value }
+                        end
+
+                        def method_missing name, *args, &amp;block
+                                return super unless args.empty?
+                                pair = Types::PropertySet::PROPERTY_MAP[name.to_s] or return super
+                                self[name]
+                        end
+                end
+
+                def meta_data
+                        @meta_data ||= MetaData.new(self)
+                end
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolestoragerb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/storage.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,3 @@
</span><ins>+require 'ole/storage/base'
+require 'ole/storage/file_system'
+require 'ole/storage/meta_data'
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101libolesupportrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/support.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/support.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/support.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,256 @@
</span><ins>+# 
+# A file with general support functions used by most files in the project.
+# 
+# These are the only methods added to other classes.
+# 
+
+require 'logger'
+require 'stringio'
+require 'enumerator'
+
+class String # :nodoc:
+        # plural of String#index. returns all offsets of +string+. rename to indices?
+        #
+        # note that it doesn't check for overlapping values.
+        def indexes string
+                # in some ways i'm surprised that $~ works properly in this case...
+                to_enum(:scan, /#{Regexp.quote string}/m).map { $~.begin 0 }
+        end
+
+        def each_chunk size
+                (length / size.to_f).ceil.times { |i| yield self[i * size, size] }
+        end
+end
+
+class File # :nodoc:
+        # for interface consistency with StringIO etc (rather than adding #stat
+        # to them). used by RangesIO.
+        def size
+                stat.size
+        end
+end
+
+class Symbol # :nodoc:
+        unless :x.respond_to? :to_proc
+                def to_proc
+                        proc { |a| a.send self }
+                end
+        end
+end
+
+module Enumerable # :nodoc:
+        unless [].respond_to? :group_by
+                # 1.9 backport
+                def group_by
+                        hash = Hash.new { |h, key| h[key] = [] }
+                        each { |item| hash[yield(item)] &lt;&lt; item }
+                        hash
+                end
+        end
+
+        unless [].respond_to? :sum
+                def sum initial=0
+                        inject(initial) { |a, b| a + b }
+                end
+        end
+end
+
+# move to support?
+class IO # :nodoc:
+        # Copy data from IO-like object +src+, to +dst+
+        def self.copy src, dst
+                until src.eof?
+                        buf = src.read(4096)
+                        dst.write buf
+                end
+        end
+end
+
+class Logger # :nodoc:
+        # A helper method for creating a +Logger+ which produce call stack
+        # in their output
+        def self.new_with_callstack logdev=STDERR
+                log = Logger.new logdev
+                log.level = WARN
+                log.formatter = proc do |severity, time, progname, msg|
+                        # find where we were called from, in our code
+                        callstack = caller.dup
+                        callstack.shift while callstack.first =~ /\/logger\.rb:\d+:in/
+                        from = callstack.first.sub(/:in `(.*?)'/, &quot;:\\1&quot;)
+                        &quot;[%s %s]\n%-7s%s\n&quot; % [time.strftime('%H:%M:%S'), from, severity, msg.to_s]
+                end
+                log
+        end
+end
+
+# Include this module into a class that defines #each_child. It should
+# maybe use #each instead, but its easier to be more specific, and use
+# an alias.
+#
+# I don't want to force the class to cache children (eg where children
+# are loaded on request in pst), because that forces the whole tree to
+# be loaded. So, the methods should only call #each_child once, and 
+# breadth first iteration holds its own copy of the children around.
+#
+# Main methods are #recursive, and #to_tree
+module RecursivelyEnumerable # :nodoc:
+        def each_recursive_depth_first(&amp;block)
+                each_child do |child|
+                        yield child
+                        if child.respond_to? :each_recursive_depth_first
+                                child.each_recursive_depth_first(&amp;block)
+                        end
+                end
+        end
+
+        # don't think this is actually a proper breadth first recursion. only first
+        # level is breadth first.
+        def each_recursive_breadth_first(&amp;block)
+                children = []
+                each_child do |child|
+                        children &lt;&lt; child if child.respond_to? :each_recursive_breadth_first
+                        yield child
+                end
+                children.each { |child| child.each_recursive_breadth_first(&amp;block) }
+        end
+
+        def each_recursive mode=:depth_first, &amp;block
+                # we always actually yield ourself (the tree root) before recursing
+                yield self
+                send &quot;each_recursive_#{mode}&quot;, &amp;block
+        end
+
+        # the idea of this function, is to allow use of regular Enumerable methods
+        # in a recursive fashion. eg:
+        #
+        #   # just looks at top level children
+        #   root.find { |child| child.some_condition? }
+        #   # recurse into all children getting non-folders, breadth first
+        #   root.recursive(:breadth_first).select { |child| !child.folder? }
+        #   # just get everything
+        #   items = root.recursive.to_a
+        #
+        def recursive mode=:depth_first
+                to_enum(:each_recursive, mode)
+        end
+
+        # streams a &quot;tree&quot; form of the recursively enumerable structure to +io+, or
+        # return a string form instead if +io+ is not specified.
+        #
+        # mostly a debugging aid. can specify a different block which will be called
+        # to provide the string form for each node.
+        def to_tree io='', &amp;inspect
+                inspect ||= :inspect.to_proc
+                io &lt;&lt; &quot;- #{inspect[self]}\n&quot;
+                recurse = proc do |node, prefix|
+                        child = nil
+                        node.each_child do |next_child|
+                                if child
+                                        io &lt;&lt; &quot;#{prefix}|- #{inspect[child]}\n&quot;
+                                        recurse.call child, prefix + '|  '
+                                end
+                                child = next_child
+                        end if node.respond_to?(:each_child)
+                        if child
+                                io &lt;&lt; &quot;#{prefix}\\- #{inspect[child]}\n&quot;
+                                recurse.call child, prefix + '   '
+                        end
+                end
+                recurse.call self, '  '
+                io
+        end
+end
+
+# can include File::Constants
+class IO
+        # this is for jruby
+        include File::Constants unless defined?(RDONLY)
+
+        # nabbed from rubinius, and modified
+        def self.parse_mode mode
+                ret = 0
+
+                case mode[0, 1]
+                when 'r'; ret |= RDONLY
+                when 'w'; ret |= WRONLY | CREAT | TRUNC
+                when 'a'; ret |= WRONLY | CREAT | APPEND
+                else raise ArgumentError, &quot;illegal access mode #{mode}&quot;
+                end
+
+                (1...mode.length).each do |i|
+                        case mode[i, 1]
+                        when '+'; ret = (ret &amp; ~(RDONLY | WRONLY)) | RDWR
+                        when 'b'; ret |= Mode::BINARY
+                        else raise ArgumentError, &quot;illegal access mode #{mode}&quot;
+                        end
+                end
+        
+                ret
+        end
+
+        class Mode
+                # ruby 1.9 defines binary as 0, which isn't very helpful.
+                # its 4 in rubinius. no longer using
+                #
+                #   BINARY = 0x4 unless defined?(BINARY)
+                #
+                # for that reason, have my own constants module here
+                module Constants
+                        include File::Constants
+                        BINARY = 0x4
+                end
+                
+                include Constants
+                NAMES = %w[rdonly wronly rdwr creat trunc append binary]
+
+                attr_reader :flags
+                def initialize flags
+                        flags = IO.parse_mode flags.to_str if flags.respond_to? :to_str
+                        raise ArgumentError, &quot;invalid flags - #{flags.inspect}&quot; unless Fixnum === flags
+                        @flags = flags
+                end
+
+                def writeable?
+                        #(@flags &amp; RDONLY) == 0
+                        (@flags &amp; 0x3) != RDONLY
+                end
+
+                def readable?
+                        (@flags &amp; WRONLY) == 0
+                end
+
+                def truncate?
+                        (@flags &amp; TRUNC) != 0
+                end
+
+                def append?
+                        (@flags &amp; APPEND) != 0
+                end
+
+                def create?
+                        (@flags &amp; CREAT) != 0
+                end
+
+                def binary?
+                        (@flags &amp; BINARY) != 0
+                end
+
+=begin
+                # revisit this
+                def apply io
+                        if truncate?
+                                io.truncate 0
+                        elsif append?
+                                io.seek IO::SEEK_END, 0
+                        end
+                end
+=end
+
+                def inspect
+                        names = NAMES.map { |name| name if (flags &amp; Mode.const_get(name.upcase)) != 0 }
+                        names.unshift 'rdonly' if (flags &amp; 0x3) == 0
+                        &quot;#&lt;#{self.class} #{names.compact * '|'}&gt;&quot;
+                end
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesbaserb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/base.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/base.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/base.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,280 @@
</span><ins>+require 'iconv'
+require 'date'
+
+require 'ole/base'
+
+module Ole # :nodoc:
+        #
+        # The Types module contains all the serialization and deserialization code for standard ole
+        # types.
+        #
+        # It also defines all the variant type constants, and symbolic names.
+        #
+        module Types
+                # for anything that we don't have serialization code for
+                class Data &lt; String
+                        def self.load str
+                                new str
+                        end
+                        
+                        def self.dump str
+                                str.to_s
+                        end
+                end
+
+                class Lpstr &lt; String
+                        def self.load str
+                                # not sure if its always there, but there is often a trailing
+                                # null byte.
+                                new str.chomp(0.chr)
+                        end
+
+                        def self.dump str
+                                # do i need to append the null byte?
+                                str.to_s
+                        end
+                end
+
+                # for VT_LPWSTR
+                class Lpwstr &lt; String
+                        FROM_UTF16 = Iconv.new 'utf-8', 'utf-16le'
+                        TO_UTF16   = Iconv.new 'utf-16le', 'utf-8'
+                        
+                        def self.load str
+                                new FROM_UTF16.iconv(str).chomp(0.chr)
+                        end
+                        
+                        def self.dump str
+                                # need to append nulls?
+                                data = TO_UTF16.iconv str
+                                # not sure if this is the recommended way to do it, but I want to treat
+                                # the resulting utf16 data as regular bytes, not characters.
+                                data.force_encoding Encoding::US_ASCII if data.respond_to? :encoding
+                                data
+                        end
+                end
+
+                # for VT_FILETIME
+                class FileTime &lt; DateTime
+                        SIZE = 8
+
+                        # DateTime.new is slow... faster version for FileTime
+                        def self.new year, month, day, hour=0, min=0, sec=0, usec=0
+                                # DateTime will remove leap and leap-leap seconds
+                                sec = 59 if sec &gt; 59
+                                if month &lt;= 2
+                                        month += 12
+                                        year  -= 1
+                                end
+                                y   = year + 4800
+                                m   = month - 3
+                                jd  = day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045
+                                fr  = hour / 24.0 + min / 1440.0 + sec / 86400.0
+                                # avoid new!, as it was actually new0 in older versions of ruby (&lt;=1.8.4?)
+                                # see issue #4. this is equivalent, but doesn't rely on the aliasing used
+                                obj = allocate
+                                obj.send :initialize, jd + fr - 0.5, 0, ITALY
+                                obj
+                        end
+
+                        def self.from_time time
+                                new(*(time.to_a[0, 6].reverse + [time.usec]))
+                        end
+
+                        def self.now
+                                from_time Time.now
+                        end
+
+                        EPOCH = new 1601, 1, 1
+
+                        #def initialize year, month, day, hour, min, sec
+
+                        # Create a +DateTime+ object from a struct +FILETIME+
+                        # (http://msdn2.microsoft.com/en-us/library/ms724284.aspx).
+                        #
+                        # Converts +str+ to two 32 bit time values, comprising the high and low 32 bits of
+                        # the 100's of nanoseconds since 1st january 1601 (Epoch).
+                        def self.load str
+                                low, high = str.to_s.unpack 'V2'
+                                # we ignore these, without even warning about it
+                                return nil if low == 0 and high == 0
+                                # the + 0.00001 here stinks a bit...
+                                seconds = (high * (1 &lt;&lt; 32) + low) / 1e7 + 0.00001
+                                EPOCH + seconds / 86400 rescue return
+                        end
+
+                        # +time+ should be able to be either a Time, Date, or DateTime.
+                        def self.dump time
+                                return 0.chr * SIZE unless time
+                                # convert whatever is given to be a datetime, to handle the large range
+                                case time
+                                when Date # this includes DateTime &amp; FileTime
+                                when Time
+                                        time = from_time time
+                                else
+                                        raise ArgumentError, 'unknown time argument - %p' % [time]
+                                end
+                                # round to milliseconds (throwing away nanosecond precision) to
+                                # compensate for using Float-based DateTime
+                                nanoseconds = ((time - EPOCH).to_f * 864000000).round * 1000
+                                high, low = nanoseconds.divmod 1 &lt;&lt; 32
+                                [low, high].pack 'V2'
+                        end
+                        
+                        def inspect
+                                &quot;#&lt;#{self.class} #{to_s}&gt;&quot;
+                        end
+                end
+
+                # for VT_CLSID
+                # Unlike most of the other conversions, the Guid's are serialized/deserialized by actually
+                # doing nothing! (eg, _load &amp; _dump are null ops)
+                # Rather, its just a string with a different inspect string, and it includes a
+                # helper method for creating a Guid from that readable form (#format).
+                class Clsid &lt; String
+                        SIZE = 16
+                        PACK = 'V v v CC C6'
+
+                        def self.load str
+                                new str.to_s
+                        end
+                        
+                        def self.dump guid
+                                return 0.chr * SIZE unless guid
+                                # allow use of plain strings in place of guids.
+                                guid['-'] ? parse(guid) : guid
+                        end
+                        
+                        def self.parse str
+                                vals = str.scan(/[a-f\d]+/i).map(&amp;:hex)
+                                if vals.length == 5
+                                        # this is pretty ugly
+                                        vals[3] = ('%04x' % vals[3]).scan(/../).map(&amp;:hex)
+                                        vals[4] = ('%012x' % vals[4]).scan(/../).map(&amp;:hex)
+                                        guid = new vals.flatten.pack(PACK)
+                                        return guid if guid.format.delete('{}') == str.downcase.delete('{}')
+                                end
+                                raise ArgumentError, 'invalid guid - %p' % str
+                        end
+
+                        def format
+                                &quot;%08x-%04x-%04x-%02x%02x-#{'%02x' * 6}&quot; % unpack(PACK)
+                        end
+                        
+                        def inspect
+                                &quot;#&lt;#{self.class}:{#{format}}&gt;&quot;
+                        end
+                end
+
+                #
+                # The OLE variant types, extracted from
+                # http://www.marin.clara.net/COM/variant_type_definitions.htm.
+                #
+                # A subset is also in WIN32OLE::VARIANT, but its not cross platform (obviously).
+                #
+                # Use like:
+                #
+                #   p Ole::Types::Variant::NAMES[0x001f] =&gt; 'VT_LPWSTR'
+                #   p Ole::Types::VT_DATE # =&gt; 7
+                #
+                # The serialization / deserialization functions should be fixed to make it easier
+                # to work with. like
+                #
+                #   Ole::Types.from_str(VT_DATE, data) # and
+                #   Ole::Types.to_str(VT_DATE, data)
+                #
+                # Or similar, rather than having to do VT_* &lt;=&gt; ad hoc class name etc as it is
+                # currently.
+                #
+                module Variant
+                        NAMES = {
+                                0x0000 =&gt; 'VT_EMPTY',
+                                0x0001 =&gt; 'VT_NULL',
+                                0x0002 =&gt; 'VT_I2',
+                                0x0003 =&gt; 'VT_I4',
+                                0x0004 =&gt; 'VT_R4',
+                                0x0005 =&gt; 'VT_R8',
+                                0x0006 =&gt; 'VT_CY',
+                                0x0007 =&gt; 'VT_DATE',
+                                0x0008 =&gt; 'VT_BSTR',
+                                0x0009 =&gt; 'VT_DISPATCH',
+                                0x000a =&gt; 'VT_ERROR',
+                                0x000b =&gt; 'VT_BOOL',
+                                0x000c =&gt; 'VT_VARIANT',
+                                0x000d =&gt; 'VT_UNKNOWN',
+                                0x000e =&gt; 'VT_DECIMAL',
+                                0x0010 =&gt; 'VT_I1',
+                                0x0011 =&gt; 'VT_UI1',
+                                0x0012 =&gt; 'VT_UI2',
+                                0x0013 =&gt; 'VT_UI4',
+                                0x0014 =&gt; 'VT_I8',
+                                0x0015 =&gt; 'VT_UI8',
+                                0x0016 =&gt; 'VT_INT',
+                                0x0017 =&gt; 'VT_UINT',
+                                0x0018 =&gt; 'VT_VOID',
+                                0x0019 =&gt; 'VT_HRESULT',
+                                0x001a =&gt; 'VT_PTR',
+                                0x001b =&gt; 'VT_SAFEARRAY',
+                                0x001c =&gt; 'VT_CARRAY',
+                                0x001d =&gt; 'VT_USERDEFINED',
+                                0x001e =&gt; 'VT_LPSTR',
+                                0x001f =&gt; 'VT_LPWSTR',
+                                0x0040 =&gt; 'VT_FILETIME',
+                                0x0041 =&gt; 'VT_BLOB',
+                                0x0042 =&gt; 'VT_STREAM',
+                                0x0043 =&gt; 'VT_STORAGE',
+                                0x0044 =&gt; 'VT_STREAMED_OBJECT',
+                                0x0045 =&gt; 'VT_STORED_OBJECT',
+                                0x0046 =&gt; 'VT_BLOB_OBJECT',
+                                0x0047 =&gt; 'VT_CF',
+                                0x0048 =&gt; 'VT_CLSID',
+                                0x0fff =&gt; 'VT_ILLEGALMASKED',
+                                0x0fff =&gt; 'VT_TYPEMASK',
+                                0x1000 =&gt; 'VT_VECTOR',
+                                0x2000 =&gt; 'VT_ARRAY',
+                                0x4000 =&gt; 'VT_BYREF',
+                                0x8000 =&gt; 'VT_RESERVED',
+                                0xffff =&gt; 'VT_ILLEGAL'
+                        }
+
+                        CLASS_MAP = {
+                                # haven't seen one of these. wonder if its same as FILETIME?
+                                #'VT_DATE' =&gt; ?,
+                                'VT_LPSTR' =&gt; Lpstr,
+                                'VT_LPWSTR' =&gt; Lpwstr,
+                                'VT_FILETIME' =&gt; FileTime,
+                                'VT_CLSID' =&gt; Clsid
+                        }
+
+                        module Constants
+                                NAMES.each { |num, name| const_set name, num }
+                        end
+                        
+                        def self.load type, str
+                                type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
+                                (CLASS_MAP[type] || Data).load str
+                        end
+                        
+                        def self.dump type, variant
+                                type = NAMES[type] or raise ArgumentError, 'unknown ole type - 0x%04x' % type
+                                (CLASS_MAP[type] || Data).dump variant
+                        end
+                end
+
+                include Variant::Constants
+                
+                # deprecated aliases, kept mostly for the benefit of ruby-msg, until
+                # i release a new version.
+                def self.load_guid str
+                        Variant.load VT_CLSID, str
+                end
+
+                def self.load_time str
+                        Variant.load VT_FILETIME, str
+                end
+
+                FROM_UTF16 = Lpwstr::FROM_UTF16
+                TO_UTF16 = Lpwstr::TO_UTF16
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesproperty_setrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/property_set.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/property_set.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types/property_set.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,165 @@
</span><ins>+require 'ole/types'
+require 'yaml'
+
+module Ole
+        module Types
+                #
+                # The PropertySet class currently supports readonly access to the properties
+                # serialized in &quot;property set&quot; streams, such as the file &quot;\005SummaryInformation&quot;,
+                # in OLE files.
+                #
+                # Think it has its roots in MFC property set serialization.
+                #
+                # See http://poi.apache.org/hpsf/internals.html for details
+                #
+                class PropertySet
+                        HEADER_SIZE = 28
+                        HEADER_PACK = &quot;vvVa#{Clsid::SIZE}V&quot;
+                        OS_MAP = {
+                                0 =&gt; :win16,
+                                1 =&gt; :mac,
+                                2 =&gt; :win32,
+                                0x20001 =&gt; :ooffice, # open office on linux...
+                        }
+
+                        # define a smattering of the property set guids. 
+                        DATA = YAML.load_file(File.dirname(__FILE__) + '/../../../data/propids.yaml').
+                                inject({}) { |hash, (key, value)| hash.update Clsid.parse(key) =&gt; value }
+
+                        # create an inverted map of names to guid/key pairs
+                        PROPERTY_MAP = DATA.inject({}) do |h1, (guid, data)|
+                                data[1].inject(h1) { |h2, (id, name)| h2.update name =&gt; [guid, id] }
+                        end
+
+                        module Constants
+                                DATA.each { |guid, (name, map)| const_set name, guid }
+                        end
+
+                        include Constants
+                        include Enumerable
+
+                        class Section
+                                include Variant::Constants
+                                include Enumerable
+
+                                SIZE = Clsid::SIZE + 4
+                                PACK = &quot;a#{Clsid::SIZE}v&quot;
+
+                                attr_accessor :guid, :offset
+                                attr_reader :length
+
+                                def initialize str, property_set
+                                        @property_set = property_set
+                                        @guid, @offset = str.unpack PACK
+                                        self.guid = Clsid.load guid
+                                        load_header
+                                end
+
+                                def io
+                                        @property_set.io
+                                end
+
+                                def load_header
+                                        io.seek offset
+                                        @byte_size, @length = io.read(8).unpack 'V2'
+                                end
+                                
+                                def [] key
+                                        each_raw do |id, property_offset|
+                                                return read_property(property_offset).last if key == id
+                                        end
+                                        nil
+                                end
+                                
+                                def []= key, value
+                                        raise NotImplementedError, 'section writes not yet implemented'
+                                end
+                                
+                                def each
+                                        each_raw do |id, property_offset|
+                                                yield id, read_property(property_offset).last
+                                        end
+                                end
+
+                        private
+
+                                def each_raw
+                                        io.seek offset + 8
+                                        io.read(length * 8).each_chunk(8) { |str| yield(*str.unpack('V2')) }
+                                end
+                                
+                                def read_property property_offset
+                                        io.seek offset + property_offset
+                                        type, value = io.read(8).unpack('V2')
+                                        # is the method of serialization here custom?
+                                        case type
+                                        when VT_LPSTR, VT_LPWSTR
+                                                value = Variant.load type, io.read(value)
+                                        # ....
+                                        end
+                                        [type, value]
+                                end
+                        end
+                                                
+                        attr_reader :io, :signature, :unknown, :os, :guid, :sections
+                        
+                        def initialize io
+                                @io = io
+                                load_header io.read(HEADER_SIZE)
+                                load_section_list io.read(@num_sections * Section::SIZE)
+                                # expect no gap between last section and start of data.
+                                #Log.warn &quot;gap between section list and property data&quot; unless io.pos == @sections.map(&amp;:offset).min
+                        end
+
+                        def load_header str
+                                @signature, @unknown, @os_id, @guid, @num_sections = str.unpack HEADER_PACK
+                                # should i check that unknown == 0? it usually is. so is the guid actually
+                                @guid = Clsid.load @guid
+                                @os = OS_MAP[@os_id] || Log.warn(&quot;unknown operating system id #{@os_id}&quot;)
+                        end
+
+                        def load_section_list str
+                                @sections = str.to_enum(:each_chunk, Section::SIZE).map { |s| Section.new s, self }
+                        end
+                        
+                        def [] key
+                                pair = PROPERTY_MAP[key.to_s] or return nil
+                                section = @sections.find { |s| s.guid == pair.first } or return nil
+                                section[pair.last]
+                        end
+                        
+                        def []= key, value
+                                pair = PROPERTY_MAP[key.to_s] or return nil
+                                section = @sections.find { |s| s.guid == pair.first } or return nil
+                                section[pair.last] = value
+                        end
+                        
+                        def method_missing name, *args, &amp;block
+                                if name.to_s =~ /(.*)=$/
+                                        return super unless args.length == 1
+                                        return super unless PROPERTY_MAP[$1]
+                                        self[$1] = args.first
+                                else
+                                        return super unless args.length == 0
+                                        return super unless PROPERTY_MAP[name.to_s]
+                                        self[name]
+                                end
+                        end
+                        
+                        def each
+                                @sections.each do |section|
+                                        next unless pair = DATA[section.guid]
+                                        map = pair.last
+                                        section.each do |id, value|
+                                                name = map[id] or next
+                                                yield name, value
+                                        end
+                                end
+                        end
+                        
+                        def to_h
+                                inject({}) { |hash, (name, value)| hash.update name.to_sym =&gt; value }
+                        end
+                end
+        end
+end
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101liboletypesrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/lib/ole/types.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,2 @@
</span><ins>+require 'ole/types/base'
+require 'ole/types/property_set'
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testoleWithDirsole"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/oleWithDirs.ole</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/oleWithDirs.ole
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtestdoc"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test.doc</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test.doc
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_SummaryInformation"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_SummaryInformation</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_SummaryInformation
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_filesystemrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_filesystem.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_filesystem.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_filesystem.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,879 @@
</span><ins>+#! /usr/bin/ruby
+
+#
+# = NOTE
+#
+# This file was originally called &quot;zipfilesystemtest.rb&quot;, and was part of
+# the test case for the &quot;rubyzip&quot; project.
+#
+# As I borrowed the smart idea of using a filesystem style interface, it
+# only seemed right that I appropriate the test case in addition :). It is
+# a testament to the cleanliness of the original api &amp; tests as to how
+# easy it was to repurpose it for this project.
+#
+# I have made some modifications to the file due to some differences in the
+# capabilities of zip vs ole, but the majority of the copyright and credit
+# still goes to Thomas. His original copyright message:
+#
+#   Copyright (C) 2002, 2003 Thomas Sondergaard
+#   rubyzip is free software; you can redistribute it and/or
+#   modify it under the terms of the ruby license.
+#
+
+TEST_DIR = File.dirname __FILE__
+$:.unshift &quot;#{TEST_DIR}/../lib&quot;
+
+require 'ole/storage/file_system'
+require 'test/unit'
+
+module ExtraAssertions
+
+        def assert_forwarded(anObject, method, retVal, *expectedArgs)
+                callArgs = nil
+                setCallArgsProc = proc { |args| callArgs = args }
+                anObject.instance_eval &lt;&lt;-&quot;end_eval&quot;
+                        alias #{method}_org #{method}
+                        def #{method}(*args)
+                                ObjectSpace._id2ref(#{setCallArgsProc.object_id}).call(args)
+                                ObjectSpace._id2ref(#{retVal.object_id})
+                                end
+                end_eval
+
+                assert_equal(retVal, yield) # Invoke test
+                assert_equal(expectedArgs, callArgs)
+        ensure
+                anObject.instance_eval &quot;alias #{method} #{method}_org&quot;
+        end
+
+end
+
+class OleFsNonmutatingTest &lt; Test::Unit::TestCase
+        def setup
+                @ole = Ole::Storage.open TEST_DIR + '/oleWithDirs.ole', 'rb'
+        end
+
+        def teardown
+                @ole.close if @ole
+        end
+
+=begin
+        def test_umask
+                assert_equal(File.umask, @ole.file.umask)
+                @ole.file.umask(0006)
+        end
+=end
+
+        def test_exists?
+                assert(! @ole.file.exists?(&quot;notAFile&quot;))
+                assert(@ole.file.exists?(&quot;file1&quot;))
+                assert(@ole.file.exists?(&quot;dir1&quot;))
+                assert(@ole.file.exists?(&quot;dir1/&quot;))
+                assert(@ole.file.exists?(&quot;dir1/file12&quot;))
+                assert(@ole.file.exist?(&quot;dir1/file12&quot;)) # notice, tests exist? alias of exists? !
+
+                @ole.dir.chdir &quot;dir1/&quot;
+                assert(!@ole.file.exists?(&quot;file1&quot;))
+                assert(@ole.file.exists?(&quot;file12&quot;))
+        end
+
+        def test_open_read
+                blockCalled = false
+                @ole.file.open(&quot;file1&quot;, &quot;r&quot;) {
+                        |f|
+                        blockCalled = true
+                        assert_equal(&quot;this is the entry 'file1' in my test archive!&quot;, 
+                                                                                f.readline.chomp)
+                }
+                assert(blockCalled)
+
+                blockCalled = false
+                @ole.dir.chdir &quot;dir2&quot;
+                @ole.file.open(&quot;file21&quot;, &quot;r&quot;) {
+                        |f|
+                        blockCalled = true
+                        assert_equal(&quot;this is the entry 'dir2/file21' in my test archive!&quot;, 
+                                                                                f.readline.chomp)
+                }
+                assert(blockCalled)
+                @ole.dir.chdir &quot;/&quot;
+                
+                assert_raise(Errno::ENOENT) {
+                        @ole.file.open(&quot;noSuchEntry&quot;)
+                }
+
+                begin
+                        is = @ole.file.open(&quot;file1&quot;)
+                        assert_equal(&quot;this is the entry 'file1' in my test archive!&quot;, 
+                                                                                is.readline.chomp)
+                ensure
+                        is.close if is
+                end
+        end
+
+        def test_new
+                begin
+                        is = @ole.file.new(&quot;file1&quot;)
+                        assert_equal(&quot;this is the entry 'file1' in my test archive!&quot;, 
+                                                                                is.readline.chomp)
+                ensure
+                        is.close if is
+                end
+                begin
+                        is = @ole.file.new(&quot;file1&quot;) {
+                                fail &quot;should not call block&quot;
+                        }
+                ensure
+                        is.close if is
+                end
+        end
+
+        # currently commented out because I've taken the approach of
+        # using implicit NameError rather than explicit NotImplementedError.
+=begin
+        def test_symlink
+                assert_raise(NotImplementedError) {
+                        @ole.file.symlink(&quot;file1&quot;, &quot;aSymlink&quot;)
+                }
+        end
+=end
+
+        def test_size
+                assert_raise(Errno::ENOENT) { @ole.file.size(&quot;notAFile&quot;) }
+                assert_equal(72, @ole.file.size(&quot;file1&quot;))
+                assert_equal(0, @ole.file.size(&quot;dir2/dir21&quot;))
+
+                assert_equal(72, @ole.file.stat(&quot;file1&quot;).size)
+                assert_equal(0, @ole.file.stat(&quot;dir2/dir21&quot;).size)
+        end
+
+        def test_size?
+                assert_equal(nil, @ole.file.size?(&quot;notAFile&quot;))
+                assert_equal(72, @ole.file.size?(&quot;file1&quot;))
+                assert_equal(nil, @ole.file.size?(&quot;dir2/dir21&quot;))
+
+                assert_equal(72, @ole.file.stat(&quot;file1&quot;).size?)
+                assert_equal(nil, @ole.file.stat(&quot;dir2/dir21&quot;).size?)
+        end
+
+        def test_file?
+                assert(@ole.file.file?(&quot;file1&quot;))
+                assert(@ole.file.file?(&quot;dir2/file21&quot;))
+                assert(! @ole.file.file?(&quot;dir1&quot;))
+                assert(! @ole.file.file?(&quot;dir1/dir11&quot;))
+
+                assert(@ole.file.stat(&quot;file1&quot;).file?)
+                assert(@ole.file.stat(&quot;dir2/file21&quot;).file?)
+                assert(! @ole.file.stat(&quot;dir1&quot;).file?)
+                assert(! @ole.file.stat(&quot;dir1/dir11&quot;).file?)
+        end
+
+=begin
+        include ExtraAssertions
+
+        def test_dirname
+                assert_forwarded(File, :dirname, &quot;retVal&quot;, &quot;a/b/c/d&quot;) { 
+                        @ole.file.dirname(&quot;a/b/c/d&quot;)
+                }
+        end
+
+        def test_basename
+                assert_forwarded(File, :basename, &quot;retVal&quot;, &quot;a/b/c/d&quot;) { 
+                        @ole.file.basename(&quot;a/b/c/d&quot;)
+                }
+        end
+
+        def test_split
+                assert_forwarded(File, :split, &quot;retVal&quot;, &quot;a/b/c/d&quot;) { 
+                        @ole.file.split(&quot;a/b/c/d&quot;)
+                }
+        end
+
+        def test_join
+                assert_equal(&quot;a/b/c&quot;, @ole.file.join(&quot;a/b&quot;, &quot;c&quot;))
+                assert_equal(&quot;a/b/c/d&quot;, @ole.file.join(&quot;a/b&quot;, &quot;c/d&quot;))
+                assert_equal(&quot;/c/d&quot;, @ole.file.join(&quot;&quot;, &quot;c/d&quot;))
+                assert_equal(&quot;a/b/c/d&quot;, @ole.file.join(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;))
+        end
+
+        def test_utime
+                t_now = Time.now
+                t_bak = @ole.file.mtime(&quot;file1&quot;)
+                @ole.file.utime(t_now, &quot;file1&quot;)
+                assert_equal(t_now, @ole.file.mtime(&quot;file1&quot;))
+                @ole.file.utime(t_bak, &quot;file1&quot;)
+                assert_equal(t_bak, @ole.file.mtime(&quot;file1&quot;))
+        end
+
+
+        def assert_always_false(operation)
+                assert(! @ole.file.send(operation, &quot;noSuchFile&quot;))
+                assert(! @ole.file.send(operation, &quot;file1&quot;))
+                assert(! @ole.file.send(operation, &quot;dir1&quot;))
+                assert(! @ole.file.stat(&quot;file1&quot;).send(operation))
+                assert(! @ole.file.stat(&quot;dir1&quot;).send(operation))
+        end
+
+        def assert_true_if_entry_exists(operation)
+                assert(! @ole.file.send(operation, &quot;noSuchFile&quot;))
+                assert(@ole.file.send(operation, &quot;file1&quot;))
+                assert(@ole.file.send(operation, &quot;dir1&quot;))
+                assert(@ole.file.stat(&quot;file1&quot;).send(operation))
+                assert(@ole.file.stat(&quot;dir1&quot;).send(operation))
+        end
+
+        def test_pipe?
+                assert_always_false(:pipe?)
+        end
+
+        def test_blockdev?
+                assert_always_false(:blockdev?)
+        end
+
+        def test_symlink?
+                assert_always_false(:symlink?)
+        end
+
+        def test_socket?
+                assert_always_false(:socket?)
+        end
+
+        def test_chardev?
+                assert_always_false(:chardev?)
+        end
+
+        def test_truncate
+                assert_raise(StandardError, &quot;truncate not supported&quot;) {
+                        @ole.file.truncate(&quot;file1&quot;, 100)
+                }
+        end
+
+        def assert_e_n_o_e_n_t(operation, args = [&quot;NoSuchFile&quot;])
+                assert_raise(Errno::ENOENT) {
+                        @ole.file.send(operation, *args)
+                }
+        end
+
+        def test_ftype
+                assert_e_n_o_e_n_t(:ftype)
+                assert_equal(&quot;file&quot;, @ole.file.ftype(&quot;file1&quot;))
+                assert_equal(&quot;directory&quot;, @ole.file.ftype(&quot;dir1/dir11&quot;))
+                assert_equal(&quot;directory&quot;, @ole.file.ftype(&quot;dir1/dir11/&quot;))
+        end
+=end
+
+        def test_directory?
+                assert(! @ole.file.directory?(&quot;notAFile&quot;))
+                assert(! @ole.file.directory?(&quot;file1&quot;))
+                assert(! @ole.file.directory?(&quot;dir1/file11&quot;))
+                assert(@ole.file.directory?(&quot;dir1&quot;))
+                assert(@ole.file.directory?(&quot;dir1/&quot;))
+                assert(@ole.file.directory?(&quot;dir2/dir21&quot;))
+
+                assert(! @ole.file.stat(&quot;file1&quot;).directory?)
+                assert(! @ole.file.stat(&quot;dir1/file11&quot;).directory?)
+                assert(@ole.file.stat(&quot;dir1&quot;).directory?)
+                assert(@ole.file.stat(&quot;dir1/&quot;).directory?)
+                assert(@ole.file.stat(&quot;dir2/dir21&quot;).directory?)
+        end
+
+=begin
+        def test_chown
+                assert_equal(2, @ole.file.chown(1,2, &quot;dir1&quot;, &quot;file1&quot;))
+                assert_equal(1, @ole.file.stat(&quot;dir1&quot;).uid)
+                assert_equal(2, @ole.file.stat(&quot;dir1&quot;).gid)
+                assert_equal(2, @ole.file.chown(nil, nil, &quot;dir1&quot;, &quot;file1&quot;))
+        end
+
+        def test_zero?
+                assert(! @ole.file.zero?(&quot;notAFile&quot;))
+                assert(! @ole.file.zero?(&quot;file1&quot;))
+                assert(@ole.file.zero?(&quot;dir1&quot;))
+                blockCalled = false
+                ZipFile.open(&quot;data/generated/5entry.zip&quot;) {
+                        |zf|
+                        blockCalled = true
+                        assert(zf.file.zero?(&quot;data/generated/empty.txt&quot;))
+                }
+                assert(blockCalled)
+
+                assert(! @ole.file.stat(&quot;file1&quot;).zero?)
+                assert(@ole.file.stat(&quot;dir1&quot;).zero?)
+                blockCalled = false
+                ZipFile.open(&quot;data/generated/5entry.zip&quot;) {
+                        |zf|
+                        blockCalled = true
+                        assert(zf.file.stat(&quot;data/generated/empty.txt&quot;).zero?)
+                }
+                assert(blockCalled)
+        end
+=end
+
+        def test_expand_path
+                assert_equal(&quot;/&quot;, @ole.file.expand_path(&quot;.&quot;))
+                @ole.dir.chdir &quot;dir1&quot;
+                assert_equal(&quot;/dir1&quot;, @ole.file.expand_path(&quot;.&quot;))
+                assert_equal(&quot;/dir1/file12&quot;, @ole.file.expand_path(&quot;file12&quot;))
+                assert_equal(&quot;/&quot;, @ole.file.expand_path(&quot;..&quot;))
+                assert_equal(&quot;/dir2/dir21&quot;, @ole.file.expand_path(&quot;../dir2/dir21&quot;))
+        end
+
+=begin
+        def test_mtime
+                assert_equal(Time.at(1027694306),
+                                                                        @ole.file.mtime(&quot;dir2/file21&quot;))
+                assert_equal(Time.at(1027690863),
+                                                                        @ole.file.mtime(&quot;dir2/dir21&quot;))
+                assert_raise(Errno::ENOENT) {
+                        @ole.file.mtime(&quot;noSuchEntry&quot;)
+                }
+
+                assert_equal(Time.at(1027694306),
+                                                                        @ole.file.stat(&quot;dir2/file21&quot;).mtime)
+                assert_equal(Time.at(1027690863),
+                                                                        @ole.file.stat(&quot;dir2/dir21&quot;).mtime)
+        end
+
+        def test_ctime
+                assert_nil(@ole.file.ctime(&quot;file1&quot;))
+                assert_nil(@ole.file.stat(&quot;file1&quot;).ctime)
+        end
+
+        def test_atime
+                assert_nil(@ole.file.atime(&quot;file1&quot;))
+                assert_nil(@ole.file.stat(&quot;file1&quot;).atime)
+        end
+
+        def test_readable?
+                assert(! @ole.file.readable?(&quot;noSuchFile&quot;))
+                assert(@ole.file.readable?(&quot;file1&quot;))
+                assert(@ole.file.readable?(&quot;dir1&quot;))
+                assert(@ole.file.stat(&quot;file1&quot;).readable?)
+                assert(@ole.file.stat(&quot;dir1&quot;).readable?)
+        end
+
+        def test_readable_real?
+                assert(! @ole.file.readable_real?(&quot;noSuchFile&quot;))
+                assert(@ole.file.readable_real?(&quot;file1&quot;))
+                assert(@ole.file.readable_real?(&quot;dir1&quot;))
+                assert(@ole.file.stat(&quot;file1&quot;).readable_real?)
+                assert(@ole.file.stat(&quot;dir1&quot;).readable_real?)
+        end
+
+        def test_writable?
+                assert(! @ole.file.writable?(&quot;noSuchFile&quot;))
+                assert(@ole.file.writable?(&quot;file1&quot;))
+                assert(@ole.file.writable?(&quot;dir1&quot;))
+                assert(@ole.file.stat(&quot;file1&quot;).writable?)
+                assert(@ole.file.stat(&quot;dir1&quot;).writable?)
+        end
+
+        def test_writable_real?
+                assert(! @ole.file.writable_real?(&quot;noSuchFile&quot;))
+                assert(@ole.file.writable_real?(&quot;file1&quot;))
+                assert(@ole.file.writable_real?(&quot;dir1&quot;))
+                assert(@ole.file.stat(&quot;file1&quot;).writable_real?)
+                assert(@ole.file.stat(&quot;dir1&quot;).writable_real?)
+        end
+
+        def test_executable?
+                assert(! @ole.file.executable?(&quot;noSuchFile&quot;))
+                assert(! @ole.file.executable?(&quot;file1&quot;))
+                assert(@ole.file.executable?(&quot;dir1&quot;))
+                assert(! @ole.file.stat(&quot;file1&quot;).executable?)
+                assert(@ole.file.stat(&quot;dir1&quot;).executable?)
+        end
+
+        def test_executable_real?
+                assert(! @ole.file.executable_real?(&quot;noSuchFile&quot;))
+                assert(! @ole.file.executable_real?(&quot;file1&quot;))
+                assert(@ole.file.executable_real?(&quot;dir1&quot;))
+                assert(! @ole.file.stat(&quot;file1&quot;).executable_real?)
+                assert(@ole.file.stat(&quot;dir1&quot;).executable_real?)
+        end
+
+        def test_owned?
+                assert_true_if_entry_exists(:owned?)
+        end
+
+        def test_grpowned?
+                assert_true_if_entry_exists(:grpowned?)
+        end
+
+        def test_setgid?
+                assert_always_false(:setgid?)
+        end
+
+        def test_setuid?
+                assert_always_false(:setgid?)
+        end
+
+        def test_sticky?
+                assert_always_false(:sticky?)
+        end
+
+        def test_stat
+                s = @ole.file.stat(&quot;file1&quot;)
+                assert(s.kind_of?(File::Stat)) # It pretends
+                assert_raise(Errno::ENOENT, &quot;No such file or directory - noSuchFile&quot;) {
+                        @ole.file.stat(&quot;noSuchFile&quot;)
+                }
+        end
+
+        def test_lstat
+                assert(@ole.file.lstat(&quot;file1&quot;).file?)
+        end
+
+
+        def test_chmod
+                assert_raise(Errno::ENOENT, &quot;No such file or directory - noSuchFile&quot;) {
+                        @ole.file.chmod(0644, &quot;file1&quot;, &quot;NoSuchFile&quot;)
+                }
+                assert_equal(2, @ole.file.chmod(0644, &quot;file1&quot;, &quot;dir1&quot;))
+        end
+
+        def test_pipe
+                assert_raise(NotImplementedError) {
+                        @ole.file.pipe
+                }
+        end
+
+        def test_foreach
+                ZipFile.open(&quot;data/generated/zipWithDir.zip&quot;) {
+                        |zf|
+                        ref = []
+                        File.foreach(&quot;data/file1.txt&quot;) { |e| ref &lt;&lt; e }
+                        
+                        index = 0
+                        zf.file.foreach(&quot;data/file1.txt&quot;) { 
+                                |l|
+                                assert_equal(ref[index], l)
+                                index = index.next
+                        }
+                        assert_equal(ref.size, index)
+                }
+                
+                ZipFile.open(&quot;data/generated/zipWithDir.zip&quot;) {
+                        |zf|
+                        ref = []
+                        File.foreach(&quot;data/file1.txt&quot;, &quot; &quot;) { |e| ref &lt;&lt; e }
+                        
+                        index = 0
+                        zf.file.foreach(&quot;data/file1.txt&quot;, &quot; &quot;) { 
+                                |l|
+                                assert_equal(ref[index], l)
+                                index = index.next
+                        }
+                        assert_equal(ref.size, index)
+                }
+        end
+
+        def test_popen
+                cmd = /mswin/i =~ RUBY_PLATFORM ? 'dir' : 'ls'
+
+                assert_equal(File.popen(cmd)          { |f| f.read }, 
+                                                                        @ole.file.popen(cmd) { |f| f.read })
+        end
+
+# Can be added later
+#  def test_select
+#    fail &quot;implement test&quot;
+#  end
+
+        def test_readlines
+                ZipFile.open(&quot;data/generated/zipWithDir.zip&quot;) {
+                        |zf|
+                        assert_equal(File.readlines(&quot;data/file1.txt&quot;), 
+                                                                                zf.file.readlines(&quot;data/file1.txt&quot;))
+                }
+        end
+
+        def test_read
+                ZipFile.open(&quot;data/generated/zipWithDir.zip&quot;) {
+                        |zf|
+                        assert_equal(File.read(&quot;data/file1.txt&quot;), 
+                                                                                zf.file.read(&quot;data/file1.txt&quot;))
+                }
+        end
+=end
+end
+
+class OleFsFileStatTest &lt; Test::Unit::TestCase
+
+        def setup
+                @ole = Ole::Storage.open TEST_DIR + '/oleWithDirs.ole', 'rb'
+        end
+
+        def teardown
+                @ole.close if @ole
+        end
+
+        def test_blocks
+                assert_equal(2, @ole.file.stat(&quot;file1&quot;).blocks)
+        end
+
+        def test_ino
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).ino)
+        end
+
+        def test_uid
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).uid)
+        end
+
+        def test_gid
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).gid)
+        end
+
+        def test_ftype
+                assert_equal(&quot;file&quot;, @ole.file.stat(&quot;file1&quot;).ftype)
+                assert_equal(&quot;directory&quot;, @ole.file.stat(&quot;dir1&quot;).ftype)
+        end
+
+=begin
+        def test_mode
+                assert_equal(0600, @ole.file.stat(&quot;file1&quot;).mode &amp; 0777)
+                assert_equal(0600, @ole.file.stat(&quot;file1&quot;).mode &amp; 0777)
+                assert_equal(0755, @ole.file.stat(&quot;dir1&quot;).mode &amp; 0777)
+                assert_equal(0755, @ole.file.stat(&quot;dir1&quot;).mode &amp; 0777)
+        end
+=end
+
+        def test_dev
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).dev)
+        end
+
+        def test_rdev
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).rdev)
+        end
+
+        def test_rdev_major
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).rdev_major)
+        end
+
+        def test_rdev_minor
+                assert_equal(0, @ole.file.stat(&quot;file1&quot;).rdev_minor)
+        end
+
+        def test_nlink
+                assert_equal(1, @ole.file.stat(&quot;file1&quot;).nlink)
+        end
+
+        def test_blksize
+                assert_equal(64, @ole.file.stat(&quot;file1&quot;).blksize)
+        end
+
+        # an additional test i added for coverage. i've tried to make the inspect
+        # string on the ole stat match that of the regular one.
+        def test_inspect
+                expect = '#&lt;Ole::Storage::FileClass::Stat ino=0, uid=0, size=72, rdev=0, nlink=1, dev=0, blocks=2, gid=0, ftype=file, blksize=64&gt;'
+                # normalize them, as instance_variables order is undefined
+                normalize = proc { |s| s[/ (.*)&gt;$/, 1].split(', ').sort.join(', ') }
+                assert_equal normalize[expect], normalize[@ole.file.stat('file1').inspect]
+        end
+end
+
+class OleFsFileMutatingTest &lt; Test::Unit::TestCase
+        def setup
+                # we use an in memory copy of the file instead of the original
+                # file based.
+                @io = StringIO.new open(TEST_DIR + '/oleWithDirs.ole', 'rb', &amp;:read)
+        end
+
+        def teardown
+                @io.close if @io
+        end

+        def test_delete
+                do_test_delete_or_unlink(:delete)
+        end
+
+        def test_unlink
+                do_test_delete_or_unlink(:unlink)
+        end
+        
+        def test_open_write
+                Ole::Storage.open(@io) {
+                        |zf|
+
+                        zf.file.open(&quot;test_open_write_entry&quot;, &quot;w&quot;) {
+                                |f|
+                                blockCalled = true
+                                f.write &quot;This is what I'm writing&quot;
+                        }
+                        assert_equal(&quot;This is what I'm writing&quot;,
+                                                                                zf.file.read(&quot;test_open_write_entry&quot;))
+
+                        # Test with existing entry
+                        zf.file.open(&quot;file1&quot;, &quot;w&quot;) {
+                                |f|
+                                blockCalled = true
+                                f.write &quot;This is what I'm writing too&quot;
+                        }
+                        assert_equal(&quot;This is what I'm writing too&quot;,
+                                                                                zf.file.read(&quot;file1&quot;))
+                }
+        end
+
+        def test_rename
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert_raise(Errno::ENOENT, &quot;&quot;) { 
+                                zf.file.rename(&quot;NoSuchFile&quot;, &quot;bimse&quot;)
+                        }
+                        zf.file.rename(&quot;file1&quot;, &quot;newNameForFile1&quot;)
+                        # lets also try moving a file to a different directory,
+                        # and renaming a directory
+                        zf.file.rename('/dir1/file11', '/dir1/dir11/file111')
+                        zf.file.rename('dir1', 'dir9')
+                }
+
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert(! zf.file.exists?(&quot;file1&quot;))
+                        assert(zf.file.exists?(&quot;newNameForFile1&quot;))
+                        assert(zf.file.exists?(&quot;dir9/dir11/file111&quot;))
+                }
+        end
+
+        def do_test_delete_or_unlink(symbol)
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert(zf.file.exists?(&quot;dir2/dir21/dir221/file2221&quot;))
+                        zf.file.send(symbol, &quot;dir2/dir21/dir221/file2221&quot;)
+                        assert(! zf.file.exists?(&quot;dir2/dir21/dir221/file2221&quot;))
+
+                        assert(zf.file.exists?(&quot;dir1/file11&quot;))
+                        assert(zf.file.exists?(&quot;dir1/file12&quot;))
+                        zf.file.send(symbol, &quot;dir1/file11&quot;, &quot;dir1/file12&quot;)
+                        assert(! zf.file.exists?(&quot;dir1/file11&quot;))
+                        assert(! zf.file.exists?(&quot;dir1/file12&quot;))
+
+                        assert_raise(Errno::ENOENT) { zf.file.send(symbol, &quot;noSuchFile&quot;) }
+                        assert_raise(Errno::EISDIR) { zf.file.send(symbol, &quot;dir1/dir11&quot;) }
+                        assert_raise(Errno::EISDIR) { zf.file.send(symbol, &quot;dir1/dir11/&quot;) }
+                }
+
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert(! zf.file.exists?(&quot;dir2/dir21/dir221/file2221&quot;))
+                        assert(! zf.file.exists?(&quot;dir1/file11&quot;))
+                        assert(! zf.file.exists?(&quot;dir1/file12&quot;))
+
+                        assert(zf.file.exists?(&quot;dir1/dir11&quot;))
+                        assert(zf.file.exists?(&quot;dir1/dir11/&quot;))
+                }
+        end
+
+end
+
+class OleFsDirectoryTest &lt; Test::Unit::TestCase
+        def setup
+                # we use an in memory copy of the file instead of the original
+                # file based.
+                @io = StringIO.new open(TEST_DIR + '/oleWithDirs.ole', 'rb', &amp;:read)
+        end
+
+        def teardown
+                @io.close if @io
+        end
+        
+        def test_delete
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert_raise(Errno::ENOENT, &quot;No such file or directory - NoSuchFile.txt&quot;) {
+                                zf.dir.delete(&quot;NoSuchFile.txt&quot;)
+                        }
+                        # see explanation below, touch a &amp;&amp; ruby -e 'Dir.delete &quot;a&quot;' gives ENOTDIR not EINVAL
+                        assert_raise(Errno::ENOTDIR, &quot;Invalid argument - file1&quot;) {
+                                zf.dir.delete(&quot;file1&quot;)
+                        }
+                        assert(zf.file.exists?(&quot;dir1&quot;))
+                        #zf.dir.delete(&quot;dir1&quot;)
+                        #assert(! zf.file.exists?(&quot;dir1&quot;))
+                        # ^ this was allowed in zipfilesystem, but my code follows Dir.delete, and requires that
+                        # the directory be empty first. need to delete recursively if you want other behaviour.
+                        assert_raises(Errno::ENOTEMPTY) { zf.dir.delete('dir1') }
+                }
+        end
+
+        def test_mkdir
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert_raise(Errno::EEXIST, &quot;File exists - dir1&quot;) { 
+                                zf.dir.mkdir(&quot;file1&quot;) 
+                        }
+                        assert_raise(Errno::EEXIST, &quot;File exists - dir1&quot;) { 
+                                zf.dir.mkdir(&quot;dir1&quot;) 
+                        }
+                        assert(!zf.file.exists?(&quot;newDir&quot;))
+                        zf.dir.mkdir(&quot;newDir&quot;)
+                        assert(zf.file.directory?(&quot;newDir&quot;))
+                        assert(!zf.file.exists?(&quot;newDir2&quot;))
+                        # FIXME - mode not supported yet
+                        #zf.dir.mkdir(&quot;newDir2&quot;, 3485)
+                        #assert(zf.file.directory?(&quot;newDir2&quot;))
+                        zf.dir.rmdir 'newDir'
+                        assert(!zf.file.exists?(&quot;newDir&quot;))
+                }
+        end
+        
+        def test_pwd_chdir_entries
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert_equal(&quot;/&quot;, zf.dir.pwd)
+
+                        assert_raise(Errno::ENOENT, &quot;No such file or directory - no such dir&quot;) {
+                                zf.dir.chdir &quot;no such dir&quot;
+                        }
+                        
+                        # changed this to ENOTDIR, which is what touch a; ruby -e &quot;Dir.chdir('a')&quot; gives you.
+                        assert_raise(Errno::ENOTDIR, &quot;Invalid argument - file1&quot;) {
+                                zf.dir.chdir &quot;file1&quot;
+                        }
+
+                        assert_equal(['.', '..', &quot;dir1&quot;, &quot;dir2&quot;, &quot;file1&quot;].sort, zf.dir.entries(&quot;.&quot;).sort)
+                        zf.dir.chdir &quot;dir1&quot;
+                        assert_equal(&quot;/dir1&quot;, zf.dir.pwd)
+                        zf.dir.chdir('dir11') { assert_equal '/dir1/dir11', zf.dir.pwd }
+                        assert_equal '/dir1', zf.dir.pwd
+                        assert_equal(['.', '..', &quot;dir11&quot;, &quot;file11&quot;, &quot;file12&quot;], zf.dir.entries(&quot;.&quot;).sort)
+                        
+                        zf.dir.chdir &quot;../dir2/dir21&quot;
+                        assert_equal(&quot;/dir2/dir21&quot;, zf.dir.pwd)
+                        assert_equal(['.', '..', &quot;dir221&quot;].sort, zf.dir.entries(&quot;.&quot;).sort)
+                }
+        end
+
+        # results here are a bit different from zip/zipfilesystem, as i've chosen to fake '.'
+        # and '..'
+        def test_foreach
+                Ole::Storage.open(@io) {
+                        |zf|
+
+                        blockCalled = false
+                        assert_raise(Errno::ENOENT, &quot;No such file or directory - noSuchDir&quot;) {
+                                zf.dir.foreach(&quot;noSuchDir&quot;) { |e| blockCalled = true }
+                        }
+                        assert(! blockCalled)
+
+                        assert_raise(Errno::ENOTDIR, &quot;Not a directory - file1&quot;) {
+                                zf.dir.foreach(&quot;file1&quot;) { |e| blockCalled = true }
+                        }
+                        assert(! blockCalled)
+
+                        entries = []
+                        zf.dir.foreach(&quot;.&quot;) { |e| entries &lt;&lt; e }
+                        assert_equal(['.', '..', &quot;dir1&quot;, &quot;dir2&quot;, &quot;file1&quot;].sort, entries.sort)
+
+                        entries = []
+                        zf.dir.foreach(&quot;dir1&quot;) { |e| entries &lt;&lt; e }
+                        assert_equal(['.', '..', &quot;dir11&quot;, &quot;file11&quot;, &quot;file12&quot;], entries.sort)
+                }
+        end
+
+=begin
+        # i've gone for NoMethodError instead.
+        def test_chroot
+                Ole::Storage.open(@io) {
+                        |zf|
+                        assert_raise(NotImplementedError) {
+                                zf.dir.chroot
+                        }
+                }
+        end
+=end
+
+        # Globbing not supported yet
+        #def test_glob
+        #  # test alias []-operator too
+        #  fail &quot;implement test&quot;
+        #end
+
+        def test_open_new
+                Ole::Storage.open(@io) {
+                        |zf|
+
+                        assert_raise(Errno::ENOTDIR, &quot;Not a directory - file1&quot;) {
+                                zf.dir.new(&quot;file1&quot;)
+                        }
+
+                        assert_raise(Errno::ENOENT, &quot;No such file or directory - noSuchFile&quot;) {
+                                zf.dir.new(&quot;noSuchFile&quot;)
+                        }
+
+                        d = zf.dir.new(&quot;.&quot;)
+                        assert_equal(['.', '..', &quot;file1&quot;, &quot;dir1&quot;, &quot;dir2&quot;].sort, d.entries.sort)
+                        d.close
+
+                        zf.dir.open(&quot;dir1&quot;) {
+                                |d2|
+                                assert_equal(['.', '..', &quot;dir11&quot;, &quot;file11&quot;, &quot;file12&quot;].sort, d2.entries.sort)
+                        }
+                }
+        end
+
+end
+
+class OleFsDirIteratorTest &lt; Test::Unit::TestCase
+        
+        FILENAME_ARRAY = [ &quot;f1&quot;, &quot;f2&quot;, &quot;f3&quot;, &quot;f4&quot;, &quot;f5&quot;, &quot;f6&quot;  ]
+
+        def setup
+                @dirIt = Ole::Storage::DirClass::Dir.new('/', FILENAME_ARRAY)
+        end
+
+        def test_close
+                @dirIt.close
+                assert_raise(IOError, &quot;closed directory&quot;) {
+                        @dirIt.each { |e| p e }
+                }
+                assert_raise(IOError, &quot;closed directory&quot;) {
+                        @dirIt.read
+                }
+                assert_raise(IOError, &quot;closed directory&quot;) {
+                        @dirIt.rewind
+                }
+                assert_raise(IOError, &quot;closed directory&quot;) {
+                        @dirIt.seek(0)
+                }
+                assert_raise(IOError, &quot;closed directory&quot;) {
+                        @dirIt.tell
+                }
+                
+        end
+
+        def test_each 
+                # Tested through Enumerable.entries
+                assert_equal(FILENAME_ARRAY, @dirIt.entries)
+        end
+
+        def test_read
+                FILENAME_ARRAY.size.times {
+                        |i|
+                        assert_equal(FILENAME_ARRAY[i], @dirIt.read)
+                }
+        end
+
+        def test_rewind
+                @dirIt.read
+                @dirIt.read
+                assert_equal(FILENAME_ARRAY[2], @dirIt.read)
+                @dirIt.rewind
+                assert_equal(FILENAME_ARRAY[0], @dirIt.read)
+        end
+        
+        def test_tell_seek
+                @dirIt.read
+                @dirIt.read
+                pos = @dirIt.tell
+                valAtPos = @dirIt.read
+                @dirIt.read
+                @dirIt.seek(pos)
+                assert_equal(valAtPos, @dirIt.read)
+        end
+
+end
+
+
+# Copyright (C) 2002, 2003 Thomas Sondergaard
+# rubyzip is free software; you can redistribute it and/or
+# modify it under the terms of the ruby license.
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_filesystem.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_mbatrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_mbat.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_mbat.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_mbat.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,41 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/storage/base'
+require 'ole/storage/file_system'
+require 'tempfile'
+
+class TestWriteMbat &lt; Test::Unit::TestCase
+        def test_write_mbat
+                Tempfile.open 'myolefile' do |temp|
+                        temp.binmode
+
+                        # this used to raise an error at flush time, due to failure to write the mbat
+                        Ole::Storage.open temp do |ole|
+                                # create a 10mb file
+                                ole.file.open 'myfile', 'w' do |f|
+                                        s = 0.chr * 1_000_000
+                                        10.times { f.write s }
+                                end
+                        end
+
+                        assert((10_000_000..10_100_000) === temp.size, 'check file size')
+
+                        Ole::Storage.open temp do |ole|
+                                assert_equal 10_000_000, ole.file.size('myfile')
+                                compare = ole.bbat.truncate[(0...ole.bbat.length).find { |i| ole.bbat[i] &gt; 50_000 }..-1]
+                                c = Ole::Storage::AllocationTable
+                                # 10_000_000 * 4 / 512 / 512 rounded up is 153. but then there is room needed to store the
+                                # bat in the bat, and the mbat too. hence 154. 
+                                expect = [c::EOC] * 2 + [c::BAT] * 154 + [c::META_BAT]
+                                assert_equal expect, compare, 'allocation table structure'
+                                # the sbat should be empty. in fact the file shouldn't exist at all, so the root's first
+                                # block should be EOC
+                                assert ole.sbat.empty?
+                                assert_equal c::EOC, ole.root.first_block
+                        end
+                end
+        end
+end
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_mbat.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_meta_datarb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_meta_data.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_meta_data.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_meta_data.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/storage/base'
+require 'ole/storage/meta_data'
+require 'ole/storage/file_system'
+
+class TestMetaData &lt; Test::Unit::TestCase
+        def test_meta_data
+                Ole::Storage.open File.dirname(__FILE__) + '/test.doc', 'rb' do |ole|
+                        assert_equal 'Charles Lowe', ole.meta_data[:doc_author]
+                        assert_equal 'Charles Lowe', ole.meta_data['doc_author']
+                        assert_equal 'Charles Lowe', ole.meta_data.to_h[:doc_author]
+                        assert_equal 'Title', ole.meta_data.doc_title
+                        assert_equal 'MSWordDoc', ole.meta_data.file_format
+                        assert_equal 'application/msword', ole.meta_data.mime_type
+                        assert_raises NotImplementedError do
+                                ole.meta_data[:doc_author] = 'New Author'
+                        end
+                end
+        end
+        
+        # this tests the other ways of getting the mime_type, than using &quot;\001CompObj&quot;,
+        # ie, relying on root clsid, and on the heuristics
+        def test_mime_type
+                ole = Ole::Storage.new StringIO.new
+                ole.root.clsid = Ole::Storage::MetaData::CLSID_EXCEL97.to_s
+                assert_equal nil, ole.meta_data.file_format
+                assert_equal 'application/vnd.ms-excel', ole.meta_data.mime_type
+                
+                ole.root.clsid = 0.chr * Ole::Types::Clsid::SIZE
+                assert_equal nil, ole.meta_data.file_format
+                assert_equal 'application/x-ole-storage', ole.meta_data.mime_type
+                
+                ole.file.open('Book', 'w') { |f| }
+                assert_equal 'application/vnd.ms-excel', ole.meta_data.mime_type
+                ole.file.open('WordDocument', 'w') { |f| }
+                assert_equal 'application/msword', ole.meta_data.mime_type
+                ole.file.open('__properties_version1.0', 'w') { |f| }
+                assert_equal 'application/vnd.ms-outlook', ole.meta_data.mime_type
+        end
+end
+
</ins></span></pre></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_property_setrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_property_set.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_property_set.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_property_set.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,40 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/types/property_set'
+
+class TestPropertySet &lt; Test::Unit::TestCase
+        include Ole::Types
+
+        def setup
+                @io = open File.dirname(__FILE__) + '/test_SummaryInformation', 'rb'
+        end
+
+        def teardown
+                @io.close
+        end
+
+        def test_property_set
+                propset = PropertySet.new @io
+                assert_equal :mac, propset.os
+                assert_equal 1, propset.sections.length
+                section = propset.sections.first
+                assert_equal 14, section.length
+                assert_equal 'f29f85e0-4ff9-1068-ab91-08002b27b3d9', section.guid.format
+                assert_equal PropertySet::FMTID_SummaryInformation, section.guid
+                assert_equal 'Charles Lowe', section.to_a.assoc(4).last
+                assert_equal 'Charles Lowe', propset.doc_author
+                assert_equal 'Charles Lowe', propset.to_h[:doc_author]
+
+                # knows the difference between existent and non-existent properties
+                assert_raise(NoMethodError) { propset.non_existent_key }
+                assert_raise(NotImplementedError) { propset.doc_author = 'New Author'}
+                assert_raise(NoMethodError) { propset.non_existent_key = 'Value'}
+                
+                # a valid property that has no value in this property set
+                assert_equal nil, propset.security
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_property_set.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_ranges_iorb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_ranges_io.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_ranges_io.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_ranges_io.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,103 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/ranges_io'
+require 'stringio'
+
+class TestRangesIO &lt; Test::Unit::TestCase
+        TEST_DIR = File.dirname __FILE__
+
+        def setup
+                # read from ourself, also using overlaps.
+                ranges = [100..200, 0..10, 100..150]
+                @io = RangesIO.new open(&quot;#{TEST_DIR}/test_ranges_io.rb&quot;), :ranges =&gt; ranges, :close_parent =&gt; true
+        end
+
+        def teardown
+                @io.close
+        end
+
+        def test_open
+                # block form
+                f = open(&quot;#{TEST_DIR}/test_ranges_io.rb&quot;)
+                assert_equal false, f.closed?
+                RangesIO.open f, :ranges =&gt; []
+                assert_equal false, f.closed?
+                RangesIO.open(f, :ranges =&gt; [], :close_parent =&gt; true) {}
+                assert_equal true, f.closed?
+        end
+
+        def test_basics
+                assert_equal 160, @io.size
+                assert_match %r{size=160}, @io.inspect
+        end
+
+        def test_truncate
+                assert_raises(NotImplementedError) { @io.size += 10 }
+        end
+
+        def test_seek
+                @io.pos = 10
+                @io.seek(-100, IO::SEEK_END)
+                @io.seek(-10, IO::SEEK_CUR)
+                @io.pos += 20
+                assert_equal 70, @io.pos
+                # seeking past the end doesn't throw an exception for normal
+                # files, even in read mode, but RangesIO does
+                assert_raises(Errno::EINVAL) { @io.seek 500 }
+                assert_raises(Errno::EINVAL) { @io.seek(-500, IO::SEEK_END) }
+                assert_raises(Errno::EINVAL) { @io.seek 1, 10 }
+        end
+
+        def test_read
+                # this will map to the start of the file:
+                @io.pos = 100
+                assert_equal '#! /usr/bi', @io.read(10)
+                # test selection of initial range, offset within that range
+                pos = 80
+                @io.seek pos
+                # test advancing of pos properly, by...
+                chunked = (0...10).map { @io.read 10 }.join
+                # given the file is 160 long:
+                assert_equal 80, chunked.length
+                @io.seek pos
+                # comparing with a flat read
+                assert_equal chunked, @io.read(80)
+        end
+
+        # should test gets, lineno, and other IO methods we want to have
+        def test_gets
+                assert_equal &quot;io'\n&quot;, @io.gets
+        end
+
+        def test_write
+                str = File.read &quot;#{TEST_DIR}/test_ranges_io.rb&quot;
+                @io = RangesIO.new StringIO.new(str), :ranges =&gt; @io.ranges
+                assert_equal &quot;io'\nrequir&quot;, str[100, 10]
+                @io.write 'testing testing'
+                assert_equal 'testing te', str[100, 10]
+                @io.seek 0
+                assert_equal 'testing te', @io.read(10)
+                # lets write over a range barrier
+                assert_equal '#! /usr/bi', str[0, 10]
+                assert_equal &quot;LE__\n\n\tdef&quot;, str[195, 10]
+                @io.write 'x' * 100
+                assert_equal 'x' * 10, str[0, 10]
+                assert_equal &quot;xxxxx\n\tdef&quot;, str[195, 10]
+                # write enough to overflow the file
+                assert_raises(IOError) { @io.write 'x' * 60 }
+        end
+        
+        def test_non_resizeable
+                # will try to truncate, which will fail
+                assert_raises NotImplementedError do
+                        @io = RangesIO.new(StringIO.new, 'w', :ranges =&gt; [])
+                end
+                # will be fine
+                @io = RangesIONonResizeable.new(StringIO.new, 'w', :ranges =&gt; [])
+                assert_equal '#&lt;IO::Mode wronly|creat&gt;', @io.instance_variable_get(:@mode).inspect
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_ranges_io.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_storagerb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_storage.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_storage.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_storage.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,220 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+#require 'rubygems'
+
+require 'test/unit'
+require 'ole/storage'
+require 'digest/sha1'
+require 'stringio'
+require 'tempfile'
+
+#
+# = TODO
+#
+# These tests could be a lot more complete.
+#
+
+# should test resizeable and migrateable IO.
+
+class TestStorageRead &lt; Test::Unit::TestCase
+        TEST_DIR = File.dirname __FILE__
+
+        def setup
+                @ole = Ole::Storage.open &quot;#{TEST_DIR}/test_word_6.doc&quot;, 'rb'
+        end
+
+        def teardown
+                @ole.close
+        end
+
+        def test_header
+                # should have further header tests, testing the validation etc.
+                assert_equal 17,  @ole.header.to_a.length
+                assert_equal 117, @ole.header.dirent_start
+                assert_equal 1,   @ole.header.num_bat
+                assert_equal 1,   @ole.header.num_sbat
+                assert_equal 0,   @ole.header.num_mbat
+        end
+        
+        def test_new_without_explicit_mode
+                open &quot;#{TEST_DIR}/test_word_6.doc&quot;, 'rb' do |f|
+                        assert_equal false, Ole::Storage.new(f).writeable
+                end
+        end
+
+        def capture_warnings
+                @warn = []
+                outer_warn = @warn
+                old_log = Ole::Log
+                old_verbose = $VERBOSE
+                begin
+                        $VERBOSE = nil
+                        Ole.const_set :Log, Object.new
+                        # restore for the yield
+                        $VERBOSE = old_verbose
+                        (class &lt;&lt; Ole::Log; self; end).send :define_method, :warn do |message|
+                                outer_warn &lt;&lt; message
+                        end
+                        yield
+                ensure
+                        $VERBOSE = nil
+                        Ole.const_set :Log, old_log
+                        $VERBOSE = old_verbose
+                end
+        end
+
+        def test_invalid
+                assert_raises Ole::Storage::FormatError do
+                        Ole::Storage.open StringIO.new(0.chr * 1024)
+                end
+                assert_raises Ole::Storage::FormatError do
+                        Ole::Storage.open StringIO.new(Ole::Storage::Header::MAGIC + 0.chr * 1024)
+                end
+                capture_warnings do
+                        head = Ole::Storage::Header.new
+                        head.threshold = 1024
+                        assert_raises NoMethodError do
+                                Ole::Storage.open StringIO.new(head.to_s + 0.chr * 1024)
+                        end
+                end
+                assert_equal ['may not be a valid OLE2 structured storage file'], @warn
+        end
+        
+        def test_inspect
+                assert_match(/#&lt;Ole::Storage io=#&lt;File:.*?test_word_6.doc&gt; root=#&lt;Dirent:&quot;Root Entry&quot;&gt;&gt;/, @ole.inspect)
+        end
+
+        def test_fat
+                # the fat block has all the numbers from 5..118 bar 117
+                bbat_table = [112] + ((5..118).to_a - [112, 117])
+                assert_equal bbat_table, @ole.bbat.reject { |i| i &gt;= (1 &lt;&lt; 32) - 3 }, 'bbat'
+                sbat_table = (1..43).to_a - [2, 3]
+                assert_equal sbat_table, @ole.sbat.reject { |i| i &gt;= (1 &lt;&lt; 32) - 3 }, 'sbat'
+        end
+
+        def test_directories
+                assert_equal 5, @ole.dirents.length, 'have all directories'
+                # a more complicated one would be good for this
+                assert_equal 4, @ole.root.children.length, 'properly nested directories'
+        end
+
+        def test_utf16_conversion
+                assert_equal 'Root Entry', @ole.root.name
+                assert_equal 'WordDocument', @ole.root.children[2].name
+        end
+
+        def test_read
+                # the regular String#hash was different on the mac, so asserting
+                # against full strings. switch this to sha1 instead of this fugly blob
+                sha1sums = %w[
+                        d3d1cde9eb43ed4b77d197af879f5ca8b8837577
+                        65b75cbdd1f94ade632baeeb0848dec2a342c844
+                        cfc230ec7515892cfdb85e4a173e0ce364094970
+                        ffd859d94647a11b693f06f092d1a2bccc59d50d
+                ]
+
+                # test the ole storage type
+                type = 'Microsoft Word 6.0-Dokument'
+                assert_equal type, (@ole.root/&quot;\001CompObj&quot;).read[32..-1][/([^\x00]+)/m, 1]
+                # i was actually not loading data correctly before, so carefully check everything here
+                assert_equal sha1sums, @ole.root.children.map { |child| Digest::SHA1.hexdigest child.read }
+        end
+
+        def test_dirent
+                dirent = @ole.root.children.first
+                assert_equal &quot;\001Ole&quot;, dirent.name
+                assert_equal 20, dirent.size
+                assert_equal '#&lt;Dirent:&quot;Root Entry&quot;&gt;', @ole.root.inspect
+                
+                # exercise Dirent#[]. note that if you use a number, you get the Struct
+                # fields.
+                assert_equal dirent, @ole.root[&quot;\001Ole&quot;]
+                assert_equal dirent.name_utf16, dirent[0]
+                assert_equal nil, @ole.root.time
+                
+                assert_equal @ole.root.children, @ole.root.to_enum(:each_child).to_a
+
+                dirent.open('r') { |f| assert_equal 2, f.first_block }
+                dirent.open('w') { |f| }
+                dirent.open('a') { |f| }
+        end
+
+        def test_delete
+                dirent = @ole.root.children.first
+                assert_raises(ArgumentError) { @ole.root.delete nil }
+                assert_equal [dirent], @ole.root.children &amp; [dirent]
+                assert_equal 20, dirent.size
+                @ole.root.delete dirent
+                assert_equal [], @ole.root.children &amp; [dirent]
+                assert_equal 0, dirent.size
+        end
+end
+
+class TestStorageWrite &lt; Test::Unit::TestCase
+        TEST_DIR = File.dirname __FILE__
+
+        def sha1 str
+                Digest::SHA1.hexdigest str
+        end
+
+        # try and test all the various things the #flush function does
+        def test_flush
+        end
+        
+        # FIXME
+        # don't really want to lock down the actual internal api's yet. this will just
+        # ensure for the time being that #flush continues to work properly. need a host
+        # of checks involving writes that resize their file bigger/smaller, that resize
+        # the bats to more blocks, that resizes the sb_blocks, that has migration etc.
+        def test_write_hash
+                io = StringIO.open open(&quot;#{TEST_DIR}/test_word_6.doc&quot;, 'rb', &amp;:read)
+                assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
+                Ole::Storage.open(io, :update_timestamps =&gt; false) { }
+                # hash changed. used to be efa8cfaf833b30b1d1d9381771ddaafdfc95305c
+                # thats because i now truncate the io, and am probably removing some trailing
+                # allocated available blocks.
+                assert_equal 'a39e3c4041b8a893c753d50793af8d21ca8f0a86', sha1(io.string)
+                # add a repack test here
+                Ole::Storage.open io, :update_timestamps =&gt; false, &amp;:repack
+                assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
+        end
+
+        def test_plain_repack
+                io = StringIO.open open(&quot;#{TEST_DIR}/test_word_6.doc&quot;, 'rb', &amp;:read)
+                assert_equal '9974e354def8471225f548f82b8d81c701221af7', sha1(io.string)
+                Ole::Storage.open io, :update_timestamps =&gt; false, &amp;:repack
+                # note equivalence to the above flush, repack, flush
+                assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
+                # lets do it again using memory backing
+                Ole::Storage.open(io, :update_timestamps =&gt; false) { |ole| ole.repack :mem }
+                # note equivalence to the above flush, repack, flush
+                assert_equal 'c8bb9ccacf0aaad33677e1b2a661ee6e66a48b5a', sha1(io.string)
+                assert_raises ArgumentError do
+                        Ole::Storage.open(io, :update_timestamps =&gt; false) { |ole| ole.repack :typo }
+                end
+        end
+
+        def test_create_from_scratch_hash
+                io = StringIO.new('')
+                Ole::Storage.open(io) { }
+                assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
+                # more repack test, note invariance
+                Ole::Storage.open io, :update_timestamps =&gt; false, &amp;:repack
+                assert_equal '6bb9d6c1cdf1656375e30991948d70c5fff63d57', sha1(io.string)
+        end
+
+        def test_create_dirent
+                Ole::Storage.open StringIO.new do |ole|
+                        dirent = Ole::Storage::Dirent.new ole, :name =&gt; 'test name', :type =&gt; :dir
+                        assert_equal 'test name', dirent.name
+                        assert_equal :dir, dirent.type
+                        # for a dirent created from scratch, type_id is currently not set until serialization:
+                        assert_equal 0, dirent.type_id
+                        dirent.to_s
+                        assert_equal 1, dirent.type_id
+                        assert_raises(ArgumentError) { Ole::Storage::Dirent.new ole, :type =&gt; :bogus }
+                end
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_storage.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_supportrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_support.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_support.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_support.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,159 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/support'
+
+class TestSupport &lt; Test::Unit::TestCase
+        TEST_DIR = File.dirname __FILE__
+
+        def test_file
+                assert_equal 4096, open(&quot;#{TEST_DIR}/oleWithDirs.ole&quot;) { |f| f.size }
+                # point is to have same interface as:
+                assert_equal 4096, StringIO.open(open(&quot;#{TEST_DIR}/oleWithDirs.ole&quot;, 'rb', &amp;:read)).size
+        end
+
+        def test_enumerable
+                expect = {0 =&gt; [2, 4], 1 =&gt; [1, 3]}
+                assert_equal expect, [1, 2, 3, 4].group_by { |i| i &amp; 1 }
+                assert_equal 10, [1, 2, 3, 4].sum
+                assert_equal %w[1 2 3 4], [1, 2, 3, 4].map(&amp;:to_s)
+        end
+
+        def test_logger
+                io = StringIO.new
+                log = Logger.new_with_callstack io
+                log.warn 'test'
+                expect = %r{^\[\d\d:\d\d:\d\d .*?test_support\.rb:\d+:test_logger\]\nWARN   test$}
+                assert_match expect, io.string.chomp
+        end
+
+        def test_io
+                str = 'a' * 5000 + 'b'
+                src, dst = StringIO.new(str), StringIO.new
+                IO.copy src, dst
+                assert_equal str, dst.string
+        end
+
+        def test_string
+                str = &quot;aa aa ||| aa aa&quot;
+                assert_equal [0, 3, 10, 13], str.indexes('aa')
+                # this is mostly a check that regexp quote is used.
+                assert_equal [6, 7, 8], str.indexes('|')
+                # note not [6, 7] - no overlaps
+                assert_equal [6], str.indexes('||')
+        end
+end
+
+class TestIOMode &lt; Test::Unit::TestCase
+        def mode s
+                IO::Mode.new s
+        end
+
+        def test_parse
+                assert_equal true,  mode('r+bbbbb').binary?
+                assert_equal false, mode('r+').binary?
+
+                assert_equal false, mode('r+').create?
+                assert_equal false, mode('r').create?
+                assert_equal true,  mode('wb').create?
+
+                assert_equal true,  mode('w').truncate?
+                assert_equal false, mode('r').truncate?
+                assert_equal false, mode('r+').truncate?
+
+                assert_equal true,  mode('r+').readable?
+                assert_equal true,  mode('r+').writeable?
+                assert_equal false, mode('r').writeable?
+                assert_equal false, mode('w').readable?
+
+                assert_equal true,  mode('a').append?
+                assert_equal false, mode('w+').append?
+        end
+
+        def test_invalid
+                assert_raises(ArgumentError) { mode 'rba' }
+                assert_raises(ArgumentError) { mode '+r' }
+        end
+        
+        def test_inspect
+                assert_equal '#&lt;IO::Mode rdonly&gt;', IO::Mode.new('r').inspect
+                assert_equal '#&lt;IO::Mode rdwr|creat|trunc|binary&gt;', IO::Mode.new('wb+').inspect
+                assert_equal '#&lt;IO::Mode wronly|creat|append&gt;', IO::Mode.new('a').inspect
+        end
+end
+
+class TestRecursivelyEnumerable &lt; Test::Unit::TestCase
+        class Container
+                include RecursivelyEnumerable
+        
+                def initialize *children
+                        @children = children
+                end
+        
+                def each_child(&amp;block)
+                        @children.each(&amp;block)
+                end
+        
+                def inspect
+                        &quot;#&lt;Container&gt;&quot;
+                end
+        end
+        
+        def setup
+                @root = Container.new(
+                        Container.new(1),
+                        Container.new(2,
+                                Container.new(
+                                        Container.new(3)
+                                )
+                        ),
+                        4,
+                        Container.new()
+                )
+        end
+
+        def test_find
+                i = 0
+                found = @root.recursive.find do |obj|
+                        i += 1
+                        obj == 4
+                end
+                assert_equal found, 4
+                assert_equal 9, i
+
+                i = 0
+                found = @root.recursive(:breadth_first).find do |obj|
+                        i += 1
+                        obj == 4
+                end
+                assert_equal found, 4
+                assert_equal 4, i
+
+                # this is to make sure we hit the breadth first child cache
+                i = 0
+                found = @root.recursive(:breadth_first).find do |obj|
+                        i += 1
+                        obj == 3
+                end
+                assert_equal found, 3
+                assert_equal 10, i
+        end
+
+        def test_to_tree
+                assert_equal &lt;&lt;-'end', @root.to_tree
+- #&lt;Container&gt;
+  |- #&lt;Container&gt;
+  |  \- 1
+  |- #&lt;Container&gt;
+  |  |- 2
+  |  \- #&lt;Container&gt;
+  |     \- #&lt;Container&gt;
+  |        \- 3
+  |- 4
+  \- #&lt;Container&gt;
+                end
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_support.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_typesrb"></a>
<div class="addfile"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_types.rb (0 => 1950)</h4>
<pre class="diff"><span>
<span class="info">--- branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_types.rb                                (rev 0)
+++ branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_types.rb        2010-05-12 16:46:04 UTC (rev 1950)
</span><span class="lines">@@ -0,0 +1,67 @@
</span><ins>+#! /usr/bin/ruby
+
+$: &lt;&lt; File.dirname(__FILE__) + '/../lib'
+
+require 'test/unit'
+require 'ole/types/base'
+
+class TestTypes &lt; Test::Unit::TestCase
+        include Ole::Types
+
+        def test_lpwstr
+                assert_equal &quot;t\000e\000s\000t\000&quot;, Lpwstr.dump('test')
+                str = Lpwstr.load &quot;t\000e\000s\000t\000&quot;
+                assert_equal 'test', str
+                assert_equal Lpwstr, str.class
+        end
+
+        def test_lpstr
+                # no null byte? probably wrong
+                assert_equal 'test', Lpstr.dump('test')
+                assert_equal 'test', Lpstr.load(&quot;test\000&quot;)
+        end
+
+        # in actual fact the same code path would be used for systime i expect
+        def test_filetime
+                # for saving, we can use Date, Time, or DateTime.
+                assert_equal &quot;\000\000\260\3077-\307\001&quot;, FileTime.dump(Time.gm(2007, 1, 1))
+                time = FileTime.load &quot;\000\000\260\3077-\307\001&quot;
+                assert_equal FileTime, time.class
+                assert_equal '2007-01-01T00:00:00+00:00', time.to_s
+                # note that if we'd used Time.local, instead of gm, we'd get a different value. eg
+                assert_equal &quot;\000\370\331\336\r-\307\001&quot;, FileTime.dump(DateTime.parse('2007-01-01 00:00 +0500'))
+                # note that it still loads up as GMT, because there's no associated time zone.
+                # essentially, i'm storing and loading times as GMT. maybe i should add in conversion to local time
+                # zone when loading
+                assert_equal '2006-12-31T19:00:00+00:00', FileTime.load(&quot;\000\370\331\336\r-\307\001&quot;).to_s
+                # test loading a bogus time
+                assert_equal nil, FileTime.load(0.chr * 8)
+                # this used to be counted as an &quot;unlikely time&quot;, and discarded. that has been removed
+                assert_equal '1700-01-01T00:00:00+00:00', FileTime.load(FileTime.dump(Date.new(1700, 1, 1))).to_s
+                assert_equal '#&lt;Ole::Types::FileTime 2006-12-31T19:00:00+00:00&gt;', FileTime.load(&quot;\000\370\331\336\r-\307\001&quot;).inspect
+        end
+
+        def test_guid
+                assert_equal &quot;\x29\x03\x02\x00\x80\x08\x07\x40\xc0\x01\x12\x34\x56\x78\x90\x46&quot;,
+                                                                 Clsid.dump('{00020329-0880-4007-c001-123456789046}')
+                assert_equal '#&lt;Ole::Types::Clsid:{00020329-0880-4007-c001-123456789046}&gt;',
+                                                                 Clsid.load(&quot;\x29\x03\x02\x00\x80\x08\x07\x40\xc0\x01\x12\x34\x56\x78\x90\x46&quot;).inspect
+        end
+
+        def test_variant
+                assert_equal &quot;\x29\x03\x02\x00\x80\x08\x07\x40\xc0\x01\x12\x34\x56\x78\x90\x46&quot;,
+                                                                 Variant.dump(VT_CLSID, '{00020329-0880-4007-c001-123456789046}')
+                assert_equal &quot;2006-12-31T19:00:00+00:00&quot;, Variant.load(VT_FILETIME, &quot;\000\370\331\336\r-\307\001&quot;).to_s
+                data = Variant.load VT_DATE, 'blahblah'
+                assert_equal Data, data.class
+                assert_equal 'blahblah', Variant.dump(VT_DATE, 'blahblah')
+        end
+        
+        # purely for the purposes of coverage, i'll test these old aliases:
+        def test_deprecated_aliases
+                assert_equal '#&lt;Ole::Types::Clsid:{00020329-0880-4007-c001-123456789046}&gt;',
+                                                                 Ole::Types.load_guid(&quot;\x29\x03\x02\x00\x80\x08\x07\x40\xc0\x01\x12\x34\x56\x78\x90\x46&quot;).inspect
+                assert_equal '2006-12-31T19:00:00+00:00', Ole::Types.load_time(&quot;\000\370\331\336\r-\307\001&quot;).to_s
+        end
+end
+
</ins><span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_types.rb
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnexecutable"></a>
<div class="addfile"><h4>Added: svn:executable</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_6doc"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_6.doc</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_6.doc
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_95doc"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_95.doc</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_95.doc
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="branchesserviceupdatesandcurationvendorgemsrubyole12101testtest_word_97doc"></a>
<div class="binary"><h4>Added: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_97.doc</h4>
<pre class="diff"><span>
<span class="cx">(Binary files differ)
</span></span></pre></div>
<span class="cx">Property changes on: branches/service-updates-and-curation/vendor/gems/ruby-ole-1.2.10.1/test/test_word_97.doc
</span><span class="cx">___________________________________________________________________
</span><a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
</div>

</body>
</html>