ruby-ole 1.2.7 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ == 1.2.8 / 2008-10-08
2
+
3
+ - check in the new fixes to the mbat support.
4
+ - update README to be a bit more useful.
5
+
1
6
  == 1.2.7 / 2008-08-12
2
7
 
3
8
  - Prepare Ole::Types::PropertySet for write support.
data/README CHANGED
@@ -1,22 +1,104 @@
1
1
  = Introduction
2
2
 
3
- For now, see the docs for the Ole::Storage class.
3
+ The ruby-ole library provides a variety of functions primarily for
4
+ working with OLE2 structured storage files, such as those produced by
5
+ Microsoft Office - eg *.doc, *.msg etc.
6
+
7
+ = Example Usage
8
+
9
+ Here are some examples of how to use the library functionality,
10
+ categorised roughly by purpose.
11
+
12
+ 1. Reading and writing files within an OLE container
13
+
14
+ The recommended way to manipulate the contents is via the
15
+ "file_system" API, whereby you use Ole::Storage instance methods
16
+ similar to the regular File and Dir class methods.
17
+
18
+ ole = Ole::Storage.open('oleWithDirs.ole', 'rb+')
19
+ p ole.dir.entries('.') # => [".", "..", "dir1", "dir2", "file1"]
20
+ p ole.file.read('file1')[0, 25] # => "this is the entry 'file1'"
21
+ ole.dir.mkdir('newdir')
22
+
23
+ 2. Accessing OLE meta data
24
+
25
+ Some convenience functions are provided for (currently read only)
26
+ access to OLE property sets and other sources of meta data.
27
+
28
+ ole = Ole::Storage.open('test_word_95.doc')
29
+ p ole.meta_data.file_format # => "MSWordDoc"
30
+ p ole.meta_data.mime_type # => "application/msword"
31
+ p ole.meta_data.doc_author.split.first # => "Charles"
32
+
33
+ 3. Raw access to underlying OLE internals
34
+
35
+ This is probably of little interest to most developers using the
36
+ library, but for some use cases you may need to drop down to the
37
+ lower level API on which the "file_system" API is constructed,
38
+ which exposes more of the format details.
39
+
40
+ <tt>Ole::Storage</tt> files can have multiple files with the same name,
41
+ or with a slash in the name, and other things that are probably
42
+ strictly invalid. This API is the only way to access those files.
43
+
44
+ You can access the header object directly:
45
+
46
+ p ole.header.num_sbat # => 1
47
+ p ole.header.magic.unpack('H*') # => ["d0cf11e0a1b11ae1"]
48
+
49
+ You can directly access the array of all Dirent objects,
50
+ including the root:
51
+
52
+ p ole.dirents.length # => 5
53
+ puts ole.root.to_tree
54
+ # =>
55
+ - #<Dirent:"Root Entry">
56
+ |- #<Dirent:"\001Ole" size=20 data="\001\000\000\002\000...">
57
+ |- #<Dirent:"\001CompObj" size=98 data="\001\000\376\377\003...">
58
+ |- #<Dirent:"WordDocument" size=2574 data="\334\245e\000-...">
59
+ \- #<Dirent:"\005SummaryInformation" size=54788 data="\376\377\000\000\001...">
60
+
61
+ You can access (through RangesIO methods, or by using the
62
+ relevant Dirent and AllocationTable methods) information like where within
63
+ the container a stream is located (these are offset/length pairs):
64
+
65
+ p ole.root["\001CompObj"].open { |io| io.ranges } # => [[0, 64], [64, 34]]
66
+
67
+ See the documentation for each class for more details.
68
+
69
+ = Thanks
70
+
71
+ * The code contained in this project was initially based on chicago's libole
72
+ (source available at http://prdownloads.sf.net/chicago/ole.tgz).
73
+
74
+ * It was later augmented with some corrections by inspecting pole, and (purely
75
+ for header definitions) gsf.
76
+
77
+ * The property set parsing code came from the apache java project POIFS.
78
+
79
+ * The excellent idea for using a pseudo file system style interface by providing
80
+ #file and #dir methods which mimic File and Dir, was borrowed (along with almost
81
+ unchanged tests!) from Thomas Sondergaard's rubyzip.
4
82
 
5
83
  = TODO
6
84
 
7
- == 1.2.8
85
+ == 1.2.9
8
86
 
9
- * fix property sets a bit more. see TODO in Ole::Storage::MetaData
87
+ * add buffering to rangesio so that performance for small reads and writes
88
+ isn't so awful. maybe try and remove the bottlenecks of unbuffered first
89
+ with more profiling, then implement the buffering on top of that.
10
90
  * fix mode strings - like truncate when using 'w+', supporting append
11
91
  'a+' modes etc. done?
12
92
  * make ranges io obey readable vs writeable modes.
13
93
  * more RangesIO completion. ie, doesn't support #<< at the moment.
14
- * ability to zero out padding and unused blocks
15
- * case insensitive mode for ole/file_system?
16
94
 
17
95
  == 1.3.1
18
96
 
19
- * fix this README :). maybe move todo out, and put something useful here.
97
+ * fix property sets a bit more. see TODO in Ole::Storage::MetaData
98
+ * ability to zero out padding and unused blocks
99
+ * case insensitive mode for ole/file_system?
100
+ * better tests for mbat support.
101
+ * further doc cleanup
20
102
 
21
103
  == Longer term
22
104
 
@@ -25,3 +107,4 @@ For now, see the docs for the Ole::Storage class.
25
107
  ole implementations (maybe perl's, and poifs) just to check its in the
26
108
  ballpark, with no remaining silly bottlenecks.
27
109
  * supposedly vba does something weird to ole files. test that.
110
+
File without changes
@@ -217,8 +217,9 @@ end
217
217
  # this subclass of ranges io explicitly ignores the truncate part of 'w' modes.
218
218
  # only really needed for the allocation table writes etc. maybe just use explicit modes
219
219
  # for those
220
- # better yet write a test that breaks before I fix it.
221
- class RangesIONonResizeable < RangesIO
220
+ # better yet write a test that breaks before I fix it. added nodoc for the
221
+ # time being.
222
+ class RangesIONonResizeable < RangesIO # :nodoc:
222
223
  def initialize io, mode='r', params={}
223
224
  mode, params = 'r', mode if Hash === mode
224
225
  flags = IO::Mode.new(mode).flags & ~IO::TRUNC
@@ -5,54 +5,8 @@ require 'ole/types'
5
5
  require 'ole/ranges_io'
6
6
 
7
7
  module Ole # :nodoc:
8
- #
9
- # = Introduction
10
8
  #
11
- # <tt>Ole::Storage</tt> is a class intended to abstract away details of the
12
- # access to OLE2 structured storage files, such as those produced by
13
- # Microsoft Office, eg *.doc, *.msg etc.
14
- #
15
- # = Usage
16
- #
17
- # Usage should be fairly straight forward:
18
- #
19
- # # get the parent ole storage object
20
- # ole = Ole::Storage.open 'myfile.msg', 'r+'
21
- # # => #<Ole::Storage io=#<File:myfile.msg> root=#<Dirent:"Root Entry">>
22
- # # read some data
23
- # ole.root[1].read 4
24
- # # => "\001\000\376\377"
25
- # # get the top level root object and output a tree structure for
26
- # # debugging
27
- # puts ole.root.to_tree
28
- # # =>
29
- # - #<Dirent:"Root Entry" size=3840 time="2006-11-03T00:52:53Z">
30
- # |- #<Dirent:"__nameid_version1.0" size=0 time="2006-11-03T00:52:53Z">
31
- # | |- #<Dirent:"__substg1.0_00020102" size=16 data="CCAGAAAAAADAAA...">
32
- # ...
33
- # |- #<Dirent:"__substg1.0_8002001E" size=4 data="MTEuMA==">
34
- # |- #<Dirent:"__properties_version1.0" size=800 data="AAAAAAAAAAABAA...">
35
- # \- #<Dirent:"__recip_version1.0_#00000000" size=0 time="2006-11-03T00:52:53Z">
36
- # |- #<Dirent:"__substg1.0_0FF60102" size=4 data="AAAAAA==">
37
- # ...
38
- # # write some data, and finish up (note that open is 'r+', so this overwrites
39
- # # but doesn't truncate)
40
- # ole.root["\001CompObj"].open { |f| f.write "blah blah" }
41
- # ole.close
42
- #
43
- # = Thanks
44
- #
45
- # * The code contained in this project was initially based on chicago's libole
46
- # (source available at http://prdownloads.sf.net/chicago/ole.tgz).
47
- #
48
- # * It was later augmented with some corrections by inspecting pole, and (purely
49
- # for header definitions) gsf.
50
- #
51
- # * The property set parsing code came from the apache java project POIFS.
52
- #
53
- # * The excellent idea for using a pseudo file system style interface by providing
54
- # #file and #dir methods which mimic File and Dir, was borrowed (along with almost
55
- # unchanged tests!) from Thomas Sondergaard's rubyzip.
9
+ # This class is the primary way the user interacts with an OLE storage file.
56
10
  #
57
11
  # = TODO
58
12
  #
@@ -67,7 +21,7 @@ module Ole # :nodoc:
67
21
  class FormatError < StandardError # :nodoc:
68
22
  end
69
23
 
70
- VERSION = '1.2.7'
24
+ VERSION = '1.2.8'
71
25
 
72
26
  # options used at creation time
73
27
  attr_reader :params
@@ -81,8 +35,8 @@ module Ole # :nodoc:
81
35
  # Low level internals, you probably shouldn't need to mess with these
82
36
  attr_reader :header, :bbat, :sbat, :sb_file
83
37
 
84
- # maybe include an option hash, and allow :close_parent => true, to be more general.
85
- # +arg+ should be either a file, or an +IO+ object, and needs to be seekable.
38
+ # +arg+ should be either a filename, or an +IO+ object, and needs to be seekable.
39
+ # +mode+ is optional, and should be a regular mode string.
86
40
  def initialize arg, mode=nil, params={}
87
41
  params, mode = mode, nil if Hash === mode
88
42
  params = {:update_timestamps => true}.merge(params)
@@ -118,6 +72,8 @@ module Ole # :nodoc:
118
72
  @io.size > 0 ? load : clear
119
73
  end
120
74
 
75
+ # somewhat similar to File.open, the open class method allows a block form where
76
+ # the Ole::Storage object is automatically closed on completion of the block.
121
77
  def self.open arg, mode=nil, params={}
122
78
  ole = new arg, mode, params
123
79
  if block_given?
@@ -150,8 +106,13 @@ module Ole # :nodoc:
150
106
 
151
107
  # create an empty bbat.
152
108
  @bbat = AllocationTable::Big.new self
153
- mbat_blocks = (0...@header.num_mbat).map { |i| i + @header.mbat_start }
154
- bbat_chain = (header_block[Header::SIZE..-1] + @bbat.read(mbat_blocks)).unpack 'V*'
109
+ bbat_chain = header_block[Header::SIZE..-1].unpack 'V*'
110
+ mbat_block = @header.mbat_start
111
+ @header.num_mbat.times do
112
+ blocks = @bbat.read([mbat_block]).unpack 'V*'
113
+ mbat_block = blocks.pop
114
+ bbat_chain += blocks
115
+ end
155
116
  # am i using num_bat in the right way?
156
117
  @bbat.load @bbat.read(bbat_chain[0, @header.num_bat])
157
118
 
@@ -268,7 +229,7 @@ module Ole # :nodoc:
268
229
  # mbat must remain contiguous.
269
230
  bbat_data_len = ((@bbat.length + num_mbat_blocks) * 4 / @bbat.block_size.to_f).ceil * @bbat.block_size
270
231
  # now storing the excess mbat blocks also increases the size of the bbat:
271
- new_num_mbat_blocks = ([bbat_data_len / @bbat.block_size - 109, 0].max * 4 / @bbat.block_size.to_f).ceil
232
+ new_num_mbat_blocks = ([bbat_data_len / @bbat.block_size - 109, 0].max * 4 / (@bbat.block_size.to_f - 4)).ceil
272
233
  if new_num_mbat_blocks != num_mbat_blocks
273
234
  # need more space for the mbat.
274
235
  num_mbat_blocks = new_num_mbat_blocks
@@ -284,13 +245,16 @@ module Ole # :nodoc:
284
245
  # now extract the info we want:
285
246
  ranges = io.ranges
286
247
  bbat_chain = @bbat.chain io.first_block
287
- # the extra mbat data is a set of contiguous blocks at the end
288
248
  io.close
289
249
  bbat_chain.each { |b| @bbat[b] = AllocationTable::BAT }
290
250
  # tack on the mbat stuff
291
- @header.mbat_start = @bbat.length # need to record this here before tacking on the mbat
292
251
  @header.num_bat = bbat_chain.length
293
- num_mbat_blocks.times { @bbat << AllocationTable::META_BAT }
252
+ mbat_blocks = (0...num_mbat_blocks).map do
253
+ block = @bbat.free_block
254
+ @bbat[block] = AllocationTable::META_BAT
255
+ block
256
+ end
257
+ @header.mbat_start = mbat_blocks.first || AllocationTable::EOC
294
258
 
295
259
  # now finally write the bbat, using a not resizable io.
296
260
  # the mode here will be 'r', which allows write atm.
@@ -299,15 +263,17 @@ module Ole # :nodoc:
299
263
  # this is the mbat. pad it out.
300
264
  bbat_chain += [AllocationTable::AVAIL] * [109 - bbat_chain.length, 0].max
301
265
  @header.num_mbat = num_mbat_blocks
302
- if num_mbat_blocks == 0
303
- @header.mbat_start = AllocationTable::EOC
304
- else
266
+ if num_mbat_blocks != 0
305
267
  # write out the mbat blocks now. first of all, where are they going to be?
306
268
  mbat_data = bbat_chain[109..-1]
307
- q = @bbat.block_size / 4
308
- mbat_data += [AllocationTable::AVAIL] *((mbat_data.length / q.to_f).ceil * q - mbat_data.length)
309
- ranges = @bbat.ranges((0...num_mbat_blocks).map { |i| @header.mbat_start + i })
310
- RangesIO.open(@io, :ranges => ranges) { |f| f.write mbat_data.pack('V*') }
269
+ # expand the mbat_data to include the linked list forward pointers.
270
+ mbat_data = mbat_data.to_enum(:each_slice, @bbat.block_size / 4 - 1).to_a.
271
+ zip(mbat_blocks[1..-1] + [nil]).map { |a, b| b ? a + [b] : a }
272
+ # pad out the last one.
273
+ mbat_data.last.push(*([AllocationTable::AVAIL] * (@bbat.block_size / 4 - mbat_data.last.length)))
274
+ RangesIO.open @io, :ranges => @bbat.ranges(mbat_blocks) do |f|
275
+ f.write mbat_data.flatten.pack('V*')
276
+ end
311
277
  end
312
278
 
313
279
  # now seek back and write the header out
@@ -561,7 +527,7 @@ module Ole # :nodoc:
561
527
  length - 1
562
528
  end
563
529
 
564
- # must return first_block
530
+ # must return first_block. modifies +blocks+ in place
565
531
  def resize_chain blocks, size
566
532
  new_num_blocks = (size / block_size.to_f).ceil
567
533
  old_num_blocks = blocks.length
@@ -3,32 +3,11 @@
3
3
  #
4
4
  # This file intends to provide file system-like api support, a la <tt>zip/zipfilesystem</tt>.
5
5
  #
6
- # Ideally, this will be the recommended interface, allowing Ole::Storage, Dir, and
7
- # Zip::ZipFile to be used exchangably. It should be possible to write recursive copy using
8
- # the plain api, such that you can copy dirs/files agnostically between any of ole docs, dirs,
9
- # and zip files.
10
- #
11
- # = Usage
12
- #
13
- # Currently you can do something like the following:
14
- #
15
- # Ole::Storage.open 'test.doc' do |ole|
16
- # ole.dir.entries '/' # => [".", "..", "\001Ole", "1Table", "\001CompObj", ...]
17
- # ole.file.read "\001CompObj" # => "\001\000\376\377\003\n\000\000\377\377..."
18
- # end
19
- #
20
- # = Notes
21
- #
22
- # <tt>Ole::Storage</tt> files can have multiple files with the same name,
23
- # or with / in the name, and other things that are probably invalid anyway.
24
- # This API is unable to access those files, but of course the core, low-
25
- # level API can.
26
- #
27
- # need to implement some more IO functions on RangesIO, like #puts, #print
28
- # etc, like AbstractOutputStream from zipfile.
29
- #
30
6
  # = TODO
31
7
  #
8
+ # - need to implement some more IO functions on RangesIO, like #puts, #print
9
+ # etc, like AbstractOutputStream from zipfile.
10
+ #
32
11
  # - check Dir.mkdir, and File.open, and File.rename, to add in filename
33
12
  # length checks (max 32 / 31 or something).
34
13
  # do the automatic truncation, and add in any necessary warnings.
@@ -1,6 +1,6 @@
1
1
  require 'ole/types/property_set'
2
2
 
3
- module Ole
3
+ module Ole
4
4
  class Storage
5
5
  #
6
6
  # The MetaData class is designed to be high level interface to all the
@@ -35,33 +35,37 @@ module Ole
35
35
  FORMAT_MAP = {
36
36
  'MSWordDoc' => :doc
37
37
  }
38
-
38
+
39
39
  CLSID_EXCEL97 = Types::Clsid.parse "{00020820-0000-0000-c000-000000000046}"
40
40
  CLSID_EXCEL95 = Types::Clsid.parse "{00020810-0000-0000-c000-000000000046}"
41
41
  CLSID_WORD97 = Types::Clsid.parse "{00020906-0000-0000-c000-000000000046}"
42
42
  CLSID_WORD95 = Types::Clsid.parse "{00020900-0000-0000-c000-000000000046}"
43
-
44
- CLSID_MAP = {
45
- CLSID_EXCEL97 => :xls,
46
- CLSID_EXCEL95 => :xls,
47
- CLSID_WORD97 => :doc,
48
- CLSID_WORD95 => :doc
49
- }
43
+
44
+ CLSID_MAP = {
45
+ CLSID_EXCEL97 => :xls,
46
+ CLSID_EXCEL95 => :xls,
47
+ CLSID_WORD97 => :doc,
48
+ CLSID_WORD95 => :doc
49
+ }
50
50
 
51
51
  MIME_TYPES = {
52
52
  :xls => 'application/vnd.ms-excel',
53
53
  :doc => 'application/msword',
54
54
  :ppt => 'application/vnd.ms-powerpoint',
55
- :msg => 'application/vnd.ms-outlook' # not registered at IANA, but sdeems most common usage
55
+ # not registered at IANA, but seems most common usage
56
+ :msg => 'application/vnd.ms-outlook',
57
+ # this is my default fallback option. also not registered at IANA.
58
+ # file(1)'s default is application/msword, which is useless...
59
+ nil => 'application/x-ole-storage'
56
60
  }
57
-
61
+
58
62
  def initialize ole
59
63
  @ole = ole
60
64
  end
61
65
 
62
66
  # i'm thinking of making file_format and mime_type available through
63
67
  # #[], #each, and #to_h also, as calculated meta data (not assignable)
64
-
68
+
65
69
  def comp_obj
66
70
  return {} unless dirent = @ole.root["\001CompObj"]
67
71
  data = dirent.read
@@ -70,7 +74,7 @@ module Ole
70
74
  # byte_order: 0xffe
71
75
  # windows_version: 0x00000a03 (win31 apparently)
72
76
  # marker: 0xffffffff
73
- compobj_version, byte_order, windows_version, marker, clsid =
77
+ compobj_version, byte_order, windows_version, marker, clsid =
74
78
  data.unpack("vvVVa#{Types::Clsid::SIZE}")
75
79
  strings = []
76
80
  i = 28
@@ -84,7 +88,7 @@ module Ole
84
88
  {:username => strings[0], :file_format => strings[1], :unknown => strings[2..-1]}
85
89
  end
86
90
  private :comp_obj
87
-
91
+
88
92
  def file_format
89
93
  comp_obj[:file_format]
90
94
  end
@@ -93,7 +97,7 @@ module Ole
93
97
  # based on the CompObj stream contents
94
98
  type = FORMAT_MAP[file_format]
95
99
  return MIME_TYPES[type] if type
96
-
100
+
97
101
  # based on the root clsid
98
102
  type = CLSID_MAP[Types::Clsid.load(@ole.root.clsid)]
99
103
  return MIME_TYPES[type] if type
@@ -103,19 +107,21 @@ module Ole
103
107
  return MIME_TYPES[:msg] if has_file['__nameid_version1.0'] or has_file['__properties_version1.0']
104
108
  return MIME_TYPES[:doc] if has_file['worddocument'] or has_file['document']
105
109
  return MIME_TYPES[:xls] if has_file['workbook'] or has_file['book']
110
+
111
+ MIME_TYPES[nil]
106
112
  end
107
-
113
+
108
114
  def [] key
109
115
  pair = Types::PropertySet::PROPERTY_MAP[key.to_s] or return nil
110
116
  file = FILE_MAP[pair.first] or return nil
111
117
  dirent = @ole.root[file] or return nil
112
118
  dirent.open { |io| return Types::PropertySet.new(io)[key] }
113
119
  end
114
-
120
+
115
121
  def []= key, value
116
122
  raise NotImplementedError, 'meta data writes not implemented'
117
123
  end
118
-
124
+
119
125
  def each(&block)
120
126
  FILE_MAP.values.each do |file|
121
127
  dirent = @ole.root[file] or next
@@ -126,7 +132,7 @@ module Ole
126
132
  def to_h
127
133
  inject({}) { |hash, (name, value)| hash.update name.to_sym => value }
128
134
  end
129
-
135
+
130
136
  def method_missing name, *args, &block
131
137
  return super unless args.empty?
132
138
  pair = Types::PropertySet::PROPERTY_MAP[name.to_s] or return super
@@ -89,7 +89,7 @@ end
89
89
  # breadth first iteration holds its own copy of the children around.
90
90
  #
91
91
  # Main methods are #recursive, and #to_tree
92
- module RecursivelyEnumerable
92
+ module RecursivelyEnumerable # :nodoc:
93
93
  def each_recursive_depth_first(&block)
94
94
  each_child do |child|
95
95
  yield child
File without changes
File without changes
@@ -32,7 +32,7 @@ class TestMetaData < Test::Unit::TestCase
32
32
 
33
33
  ole.root.clsid = 0.chr * Ole::Types::Clsid::SIZE
34
34
  assert_equal nil, ole.meta_data.file_format
35
- assert_equal nil, ole.meta_data.mime_type
35
+ assert_equal 'application/x-ole-storage', ole.meta_data.mime_type
36
36
 
37
37
  ole.file.open('Book', 'w') { |f| }
38
38
  assert_equal 'application/vnd.ms-excel', ole.meta_data.mime_type
File without changes
File without changes
@@ -173,7 +173,8 @@ class TestStorageRead < Test::Unit::TestCase
173
173
 
174
174
  def test_dirent
175
175
  dirent = @ole.root.children.first
176
- assert_equal '#<Dirent:"\001Ole" size=20 data="\001\000\000\002\000...">', dirent.inspect
176
+ assert_equal "\001Ole", dirent.name
177
+ assert_equal 20, dirent.size
177
178
  assert_equal '#<Dirent:"Root Entry">', @ole.root.inspect
178
179
 
179
180
  # exercise Dirent#[]. note that if you use a number, you get the Struct
File without changes
File without changes
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-ole
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.7
5
- platform: ""
4
+ version: 1.2.8
5
+ platform: ruby
6
6
  authors:
7
7
  - Charles Lowe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-12 00:00:00 +10:00
12
+ date: 2008-10-08 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,29 +27,29 @@ files:
27
27
  - ChangeLog
28
28
  - data/propids.yaml
29
29
  - bin/oletool
30
+ - lib/ole/support.rb
30
31
  - lib/ole/base.rb
32
+ - lib/ole/storage.rb
31
33
  - lib/ole/file_system.rb
32
34
  - lib/ole/ranges_io.rb
33
35
  - lib/ole/storage/base.rb
34
36
  - lib/ole/storage/file_system.rb
35
37
  - lib/ole/storage/meta_data.rb
36
- - lib/ole/storage.rb
37
- - lib/ole/support.rb
38
+ - lib/ole/types.rb
38
39
  - lib/ole/types/base.rb
39
40
  - lib/ole/types/property_set.rb
40
- - lib/ole/types.rb
41
+ - test/test_types.rb
41
42
  - test/test_filesystem.rb
42
- - test/test_mbat.rb
43
+ - test/test_support.rb
44
+ - test/test_storage.rb
43
45
  - test/test_meta_data.rb
44
- - test/test_property_set.rb
45
46
  - test/test_ranges_io.rb
46
- - test/test_storage.rb
47
- - test/test_support.rb
48
- - test/test_types.rb
47
+ - test/test_property_set.rb
48
+ - test/test_mbat.rb
49
49
  - test/test.doc
50
50
  - test/test_word_6.doc
51
- - test/test_word_95.doc
52
51
  - test/test_word_97.doc
52
+ - test/test_word_95.doc
53
53
  - test/oleWithDirs.ole
54
54
  - test/test_SummaryInformation
55
55
  has_rdoc: true
@@ -79,16 +79,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  requirements: []
80
80
 
81
81
  rubyforge_project: ruby-ole
82
- rubygems_version: 0.9.5
82
+ rubygems_version: 1.2.0
83
83
  signing_key:
84
84
  specification_version: 2
85
85
  summary: Ruby OLE library.
86
86
  test_files:
87
+ - test/test_types.rb
87
88
  - test/test_filesystem.rb
88
- - test/test_mbat.rb
89
+ - test/test_support.rb
90
+ - test/test_storage.rb
89
91
  - test/test_meta_data.rb
90
- - test/test_property_set.rb
91
92
  - test/test_ranges_io.rb
92
- - test/test_storage.rb
93
- - test/test_support.rb
94
- - test/test_types.rb
93
+ - test/test_property_set.rb
94
+ - test/test_mbat.rb