fsdb 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,13 @@
1
+ fsdb 0.7.0
2
+
3
+ - remove fcntl locking option; fsdb is pure ruby again.
4
+
5
+ - removed obsolete mutex classes; ruby 1.8.7 and 1.9.3 don't need them.
6
+
7
+ - fixed problems in Modex class under ruby 1.9.3.
8
+
9
+ - removed workaround code for obsolete ruby versions.
10
+
1
11
  fsdb 0.6.1
2
12
 
3
13
  - Improved gem packaging.
@@ -1,71 +1,58 @@
1
1
 
2
- = What is FSDB?
2
+ # What is FSDB?
3
3
 
4
- FSDB is a file system data base. FSDB provides a thread-safe, process-safe Database class which uses the native file system as its back end and allows multiple file formats and serialization methods. Users access objects in terms of their paths relative to the base directory of the database. It's very light weight (the state of a Database is essentially just a path string, and code size is very small, under 1K lines, all ruby).
4
+ FSDB is a file system data base. FSDB provides a thread-safe, process-safe Database class which uses the native file system as its back end and allows multiple file formats and serialization methods. Users access objects in terms of their paths relative to the base directory of the database. It's very light weight (the per-process state of a Database, excluding cached data, is essentially just a path string, and code size is very small, under 1K lines, all ruby).
5
5
 
6
- FSDB stores bundles of ruby objects at nodes in the file system. Each bundle is saved and restored as a whole, so internal references persist as usual. These bundles are the atoms of transactions. References between bundles are handled through path strings. The format of each bundle on disk can vary; format classes for plain text strings, marshalled data, and yaml data are included, but FSDB can easily be extended to recognize other formats, both binary and text. FSDB treats directories as collections and provides directory iterator methods.
6
+ FSDB stores data at nodes in the file system. The format can vary depending on type. For example, the default file type can be read into your program as a string, but files with the .obj suffix could be read using marshal, and files with the .yaml suffix as yaml. FSDB can easily be extended to recognize other formats, both binary and text. FSDB treats directories as collections and provides directory iterator methods. Files are the atoms of transactions: each file is saved and restored as a whole. References between objects stored in different files can be persisted as path strings.
7
7
 
8
8
  FSDB has been tested on a variety of platforms and ruby versions, and is not known to have any problems. (On WindowsME/98/95, multiple processes can access a database unsafely, because flock() is not available on the platform.) See the Testing section for details.
9
9
 
10
10
  FSDB does not yet have any indexing or querying mechanisms, and is probably missing many other useful database features, so it is not a general replacement for RDBs or OODBs. However, if you are looking for a lightweight, concurrent object store with reasonable performance and better granularity than PStore, in pure Ruby, with a Ruby license, take a look at FSDB. Also, if you are looking for an easy way of making an existing file tree look like a database, especially if it has heterogeneous file formats, FSDB might be useful.
11
11
 
12
12
 
13
- == Installation
13
+ ## Synopsis
14
14
 
15
- For .gem:
15
+ ```ruby
16
+ require 'fsdb'
16
17
 
17
- gem install fsdb
18
+ db = FSDB::Database.new('/tmp/my-data')
18
19
 
19
- For .tgz, unpack and then:
20
+ db['recent-movies/myself'] = ["The King's Speech", "Harry Potter 7"]
21
+ puts db['recent-movies/myself'][1] # ==> "Harry Potter 7"
20
22
 
21
- ruby install.rb config
22
- ruby install.rb setup
23
- ruby install.rb install
23
+ db.edit 'recent-movies/myself' do |movies|
24
+ movies << "The Muppets"
25
+ end
26
+ ```
24
27
 
25
- == Synopsis
26
-
27
- require 'fsdb'
28
-
29
- db = FSDB::Database.new('/tmp/my-data')
30
-
31
- db['recent-movies/myself'] = ["LOTR II", "Austin Powers"]
32
- puts db['recent-movies/myself'][0] # ==> "LOTR II"
33
-
34
- db.edit 'recent-movies/myself' do |list|
35
- list << "A la recherche du temps perdu"
36
- end
37
28
 
38
-
39
- == Path names
29
+ ## Path names
40
30
 
41
31
  Keys in the database are path strings, which are simply strings in the usual forward-slash delimited format, relative to the database's directory. There are some points to be aware of when using them to refer to database objects.
42
32
 
43
33
  * Paths to directories are formed in one of two ways:
44
34
 
45
- - explicitly, with a trailing slash, as in <tt>db['foo/']</tt>
35
+ - explicitly, with a trailing slash, as in `db['foo/']`
46
36
 
47
- - implicitly, as in <tt>db['foo']</tt> if foo is already a directory, or as
48
- in <tt>db['foo/bar']</tt>, which creates <tt>'foo'</tt> if it did not
37
+ - implicitly, as in `db['foo']` if `foo` is already a directory, or as
38
+ in `db['foo/bar']`, which creates `foo` if it did not
49
39
  already exist.
50
40
 
51
- The root dir of the database is simply <tt>/</tt>, its child directories are
52
- of the form <tt>foo/</tt> and so on. The leading and trailing slashes are
41
+ * The root dir of the database is simply `/`, its child directories are
42
+ of the form `foo/` and so on. The leading and trailing slashes are
53
43
  both optional.
54
44
 
55
45
  * Objects can be stored in various formats, indicated by path name. A typical
56
46
  mapping might be:
57
47
 
58
- <tt>foo.obj</tt>:: Marshalled data (the default for unrecognized extension)
59
-
60
- <tt>foo.txt</tt>:: String
61
-
62
- <tt>foo/</tt>:: Directory (the contents is presented to the caller as
63
- a list of file and subdirectory paths that can be used in
64
- browse, edit, etc.)
48
+ file name | de-serialized data type
49
+ --------- | --------------
50
+ `foo.obj` | Marshalled data
51
+ `foo.txt` | String
52
+ `foo/` | Directory (the contents is presented to the caller as a list of file and subdirectory paths that can be used in browse, edit, etc.)
53
+ `foo.yml` | YAML data--see examples/yaml.rb
65
54
 
66
- <tt>foo.yml</tt>:: YAML data--see examples/yaml.rb
67
-
68
- New formats, which correlate filename pattern with serialization behavior,
55
+ New formats, which correlate filename pattern with serialization behavior,
69
56
  can be defined and plugged in to databases. Each format has its own rules for
70
57
  matching patterns in the file name and recognizing the file. Patterns can be
71
58
  anything with a #=== method (such as a regex). See lib/fsdb/formats.rb
@@ -74,43 +61,48 @@ Keys in the database are path strings, which are simply strings in the usual for
74
61
 
75
62
  * Different notations for the same path, such as
76
63
 
64
+ ```
77
65
  /foo/bar
78
66
  foo/bar
79
67
  foo//bar
80
68
  foo/../foo/bar
69
+ ```
81
70
 
82
- work correctly (they access the same objects), as do paths that denote hard
71
+ work correctly (they access the same objects), as do paths that denote hard
83
72
  or soft links, if supported on the platform.
84
73
 
85
- Links are subject to the same naming convention as normal files with regard
74
+ Links are subject to the same naming convention as normal files with regard
86
75
  to format identification: format is determined by the path within the
87
76
  database used to access the object. Using a different name for a link can
88
77
  be useful if you need to access the file using two different formats (e.g.,
89
- plain text via 'foo.txt' and tabular data via 'foo.table' or whatever).
78
+ plain text via `foo.txt` and tabular CSV or TSV data via `foo.table` or
79
+ whatever).
90
80
 
91
81
  * Accessing objects in a database is unaffected by the current dir of your
92
82
  process. The database knows it's own absolute path, and path arguments to
93
83
  the Database API are interpreted relative to that. If you want to work with a
94
84
  subdirectory of the database, and paths relative to that, use Database#subdb:
95
85
 
86
+ ``` ruby
96
87
  db = Database.new['/tmp']
97
88
  db['foo/bar'] = 1
98
89
  foo = db.subdb('foo')
99
90
  foo['bar'] # ==> 1
91
+ ```
100
92
 
101
- * Paths that are outside the database ('../../zap') are allowed, but may or may
93
+ * Paths that are outside the database (`../../zap`) are allowed, but may or may
102
94
  not be desirable. Use #valid? and #validate in util.rb to check for them.
103
95
 
104
- * Directories are created when needed. So db['a/b/c'] = 1 creates two dirs and
96
+ * Directories are created when needed. So `db['a/b/c'] = 1` creates two dirs and
105
97
  one file.
106
98
 
107
- * Files beginning with '..' are ignored by fsdb dir iterators, though they
99
+ * Files beginning with `..` are ignored by fsdb dir iterators, though they
108
100
  can still be accessed in transaction operators. Some such files
109
- (<tt>..fsdb.meta.<filename></tt>) are used internally. All others _not_
110
- beginning with <tt>..fsdb</tt> are reserved for applications to use.
101
+ (`..fsdb.meta.<filename>`) are used internally. All others _not_
102
+ beginning with `..fsdb` are reserved for applications to use.
111
103
 
112
- The <tt>..fsdb.meta.<filename></tt> file holds a version number for
113
- <filename>, which is used along with mtime to check for changes (mtime
104
+ The `..fsdb.meta.<filename>` file holds a version number for
105
+ `<filename>`, which is used along with mtime to check for changes (mtime
114
106
  usually has a precision of only 1 second). In the future, the file may also
115
107
  be used to hold other metadata. (The meta file is only created when a file is
116
108
  written to and does not need to be created in advance when using existing
@@ -119,7 +111,7 @@ Keys in the database are path strings, which are simply strings in the usual for
119
111
  * util.rb has directory iterators, path globbing, and other useful tools.
120
112
 
121
113
 
122
- == Transactions
114
+ ## Transactions
123
115
 
124
116
  FSDB transactions are thread-safe and process-safe. They can be nested for
125
117
  larger-grained transactions; it is the user's responsibility to avoid deadlock.
@@ -128,38 +120,45 @@ FSDB is ACID (atomic/consistent/isolated/durable) to the extent that the underly
128
120
 
129
121
  There are two kinds of transactions:
130
122
 
131
- - A simple transfer of a value, as in <tt>db['x']</tt> and <tt>db['x'] = 1</tt>.
123
+ - A simple transfer of a value, as in `db['x']` and `db['x'] = 1`.
132
124
 
133
- Note that a sequence of such transactions is not itself a transaction, and
125
+ Note that a sequence of such transactions is not itself a transaction, and
134
126
  can be affected by other processes and threads.
135
127
 
128
+ ``` ruby
136
129
  db['foo/bar'] = [1,2,3]
137
- db['foo/bar'] += [4] # This is actually 2 transactions
130
+ db['foo/bar'] += [4] # This line is actually 2 transactions
138
131
  db['foo/bar'][-1]
132
+ ```
139
133
 
140
- It is possible for the result of these transactions to be <tt>4</tt>. But, if
134
+ It is possible for the result of these transactions to be `4`. But, if
141
135
  other threads or processes are scheduled during this code fragment, the
142
136
  result could be a completely different value, or the code could raise an
143
- method-missing exception because the object at the path has been replaced
144
- with one that does not have the <tt>+</tt> method or the <tt>[ ]</tt> method.
145
- The four operations are atomic by themselves, but the sequence is not.
137
+ method_missing exception because the object at the path has been replaced
138
+ with one that does not have the `+` method or the `[ ]` method.
139
+ The four operations are each atomic by themselves, but the sequence is not.
146
140
 
147
- Note that changes to a database object using this kind of transaction cannot
148
- be made using destructive methods (such as <tt><<</tt>) but only by
149
- assignments of the form <tt>db[<path>] = <data></tt>. Note that <tt>+=</tt>
141
+ Note that changes to a database object using this kind of transaction cannot
142
+ be made using destructive methods (such as `<<`) but only by
143
+ assignments of the form `db[<path>] = <data>`. Note that `+=`
150
144
  and similar "assignment operators" can be used but are not atomic, because
151
145
 
146
+ ``` ruby
152
147
  db[<path>] += 1
148
+ ```
149
+
150
+ is really
153
151
 
154
- is really
155
-
152
+ ``` ruby
156
153
  db[<path>] = db[<path>] + 1
154
+ ```
157
155
 
158
- So another thread or process could change the value stored at +path+ while
156
+ So another thread or process could change the value stored at `path` while
159
157
  the addition is happening.
160
158
 
161
159
  - Transactions that allow more complex interaction:
162
160
 
161
+ ``` ruby
163
162
  path = 'foo/bar'
164
163
  db[path] = [1,2,3]
165
164
 
@@ -167,14 +166,15 @@ There are two kinds of transactions:
167
166
  bar += [4]
168
167
  bar[-1]
169
168
  end
169
+ ```
170
170
 
171
- This guarantees that, if the object at the path is still <tt>[1, 2, 3]</tt>
171
+ This guarantees that, if the object at the path is still `[1, 2, 3]`
172
172
  at the time of the #edit call, the value returned by the transaction will be
173
173
  4.
174
174
 
175
- Simply put, #edit allows exclusive write access to the object at the path for
176
- the duration of the block. Other threads or processes that use FSDB methods
177
- to read or write the object will be blocked for the duration of the
175
+ Simply put, #edit allows exclusive write access to the object at the path
176
+ for the duration of the block. Other threads or processes that use FSDB
177
+ methods to read or write the object will be blocked for the duration of the
178
178
  transaction. There is also #browse, which allows read access shared by any
179
179
  number of threads and processes, and #replace, which also allows exclusive
180
180
  write access like #edit. The differences between #replace and #edit are:
@@ -184,29 +184,30 @@ There are two kinds of transactions:
184
184
  (The new value in #replace's block can be a modification of the old value,
185
185
  or an entirely different object.)
186
186
 
187
- - #replace yields +nil+ if there is no preexisting object, whereas #edit
187
+ - #replace yields `nil` if there is no preexisting object, whereas #edit
188
188
  calls #default_edit (which by default calls #object_missing, which by
189
189
  default throws MissingObjectError).
190
190
 
191
191
  - #edit is useless over a drb connection, since is it operating on a
192
192
  Marshal.dump-ed copy. Use #replace with drb.
193
193
 
194
- You can delete an object from the database (and the file system) with
195
- #delete, which returns the object. Also, #delete can take a block, which can
196
- examine the object and abort the transaction to prevent deletion. (The
197
- delete transaction has the same exclusion semantics as #edit and #replace.)
194
+ You can delete an object from the database (and the file system) with the
195
+ #delete method, which returns the object. Also, #delete can take a block,
196
+ which can examine the object and abort the transaction to prevent deletion.
197
+ (The delete transaction has the same exclusion semantics as edit and
198
+ replace.)
198
199
 
199
- The #fetch and #insert methods are aliased with <tt>[ ]</tt> and
200
- <tt>[ ]=</tt>.
200
+ The #fetch and #insert methods are aliased with `[ ]` and
201
+ `[ ]=`.
201
202
 
202
- When the object at the path specified in a transaction does not exist in the
203
+ When the object at the path specified in a transaction does not exist in the
203
204
  file system, the different transaction methods behave differently:
204
205
 
205
206
  - #browse calls #default_browse, which, in Database's implementation, calls
206
- #object_missing, which raises Database::MissingObjectError.
207
+ object_missing, which raises Database::MissingObjectError.
207
208
 
208
209
  - #edit calls #default_edit, which, in Database's implementation, calls
209
- #object_missing, which raises Database::MissingObjectError.
210
+ object_missing, which raises Database::MissingObjectError.
210
211
 
211
212
  - #replace and #insert (and #[]) ignore any missing file.
212
213
 
@@ -216,28 +217,30 @@ There are two kinds of transactions:
216
217
  - #fetch calls #default_fetch, which, in Database's implementation, returns
217
218
  nil.
218
219
 
219
- Transactions can be nested. However, the order in which objects are locked
220
+ Transactions can be nested. However, the order in which objects are locked
220
221
  can lead to deadlock if, for example, the nesting is cyclic, or two threads
221
222
  or processes request the same set of locks in a different order. One approach
222
223
  is to only request nested locks on paths in the lexicographic order of the
223
224
  path strings: "foo/bar", "foo/baz", ...
224
225
 
225
- A transaction can be aborted with Database#abort and Database.abort, after
226
+ A transaction can be aborted with Database#abort and Database.abort, after
226
227
  which the state of the object in the database remains as before the
227
228
  transaction. An exception that is raised but not handled within a
228
229
  transaction also aborts the transaction.
229
230
 
230
- Note that there is no locking on directories, but you can designate a lock
231
+ Note that there is no locking on directories, but you can designate a lock
231
232
  file for each dir and effectively have multiple-reader, single writer
232
233
  (advisory) locking on dirs. Just make sure you enclose your dir operation
233
234
  in a transaction on the lock object, and always access these objects using
234
235
  this technique.
235
236
 
237
+ ``` ruby
236
238
  db.browse('lock for dir') do
237
239
  db['dir/x'] = 1
238
240
  end
241
+ ```
239
242
 
240
- == Guarding against concurrency problems
243
+ ## Guarding against concurrency problems
241
244
 
242
245
  - It's the user's responsibility to avoid deadlock. See above.
243
246
 
@@ -247,20 +250,16 @@ There are two kinds of transactions:
247
250
 
248
251
  - It is not safe to fork while in a transaction.
249
252
 
250
- - A database can be configured to use fcntl locking instead of ruby's usual
251
- flock call. This is necessary for Linux NFS, for example. There doesn't seem
252
- to be any performance difference when running on a local filesystem.
253
-
254
- == Limitations
253
+ ## Limitations
255
254
 
256
255
  - Transactions are not journaled. There's no commit, undo, or versioning. (You
257
256
  can abort a transaction, however.) These could be added...
258
257
 
259
- == Testing
258
+ ## Testing
260
259
 
261
260
  FSDB has been tested on the following platforms and file systems:
262
261
 
263
- - Linux/x86 (single and dual cpu, ext3fs and reiserfs)
262
+ - Linux/x86 (single and dual cpu, ext3, ext4, and reiser file systems)
264
263
 
265
264
  - Solaris/sparc (dual and quad cpu, nfs and ufs)
266
265
 
@@ -270,7 +269,7 @@ FSDB has been tested on the following platforms and file systems:
270
269
 
271
270
  - Windows ME (single cpu, FAT32)
272
271
 
273
- FSDB is currently tested with ruby-1.9.1 and ruby-1.8.6.
272
+ FSDB is currently tested with ruby-1.9.3 and ruby-1.8.7.
274
273
 
275
274
  On windows, both the mswin32 and mingw32 builds of ruby have been used with FSDB. It has never been tested with cygwin or bccwin.
276
275
 
@@ -278,7 +277,7 @@ The tests include unit and stress tests. Unit tests isolate individual features
278
277
 
279
278
  The only known testing failure is on Windows ME (and presumably 95 and 98). The stress test succeeds with one process and multiple threads. It succeeds with multiple processes each with one thread. However, with two processes each with two threads, the test usually deadlocks very quickly.
280
279
 
281
- == Performance
280
+ ## Performance
282
281
 
283
282
  FSDB is not very fast. It's useful more for its safety, flexibility, and ease of use.
284
283
 
@@ -295,16 +294,16 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
295
294
  - On an 850MHz PIII under linux, with debugging turned off (-b option),
296
295
  test-concurrency.rb reports:
297
296
 
298
- processes threads objects transactions per cpu second
299
- ---------------------------------------------------------------
300
- 1 1 10 965
301
- 1 10 10 165
302
- 10 1 10 684
303
- 10 10 10 122
304
- 10 10 100 100
305
- 10 10 10000 92
297
+ processes | threads | objects | transactions per cpu second
298
+ --------- | --------- | --------- | ---------------------------
299
+ 1 | 1 | 10 | 965
300
+ 1 | 10 | 10 | 165
301
+ 10 | 1 | 10 | 684
302
+ 10 | 10 | 10 | 122
303
+ 10 | 10 | 100 | 100
304
+ 10 | 10 | 10000 | 92
306
305
 
307
- These results are not representative of typical applications, because the
306
+ These results are not representative of typical applications, because the
308
307
  test was designed to stress the database and expose stability problems, not
309
308
  to immitate typical use of database-stored objects. See bench/bench.rb for
310
309
  for bechmarks.
@@ -314,23 +313,23 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
314
313
  clear out the cache's reference to the object so that it will be loaded
315
314
  freshly the next time #fetch is called on the path.
316
315
 
317
- The performance hit of #fetch is of course greater with larger objects,
316
+ The performance hit of #fetch is of course greater with larger objects,
318
317
  and with objects that are loaded by a more complex procedure, such as
319
318
  Marshal.load.
320
319
 
321
- You can think of #fetch as a "deep copy" of the object. If you call it twice,
322
- you get different copies that do not share any parts. Or you can think of it
323
- as File.read--it gives you an instantaneous snapshot of the file, but does
324
- not give you a transaction "window" in which no other thread or process can
325
- modify the object.
320
+ You can think of #fetch as a "deep copy" of the object. If you call it
321
+ twice, you get different copies that do not share any parts. Or you can think
322
+ of it as File.read--it gives you an instantaneous snapshot of the file, but
323
+ does not give you a transaction "window" in which no other thread or process
324
+ can modify the object.
326
325
 
327
- There is no analogous concern with #insert and its alias #[]=. These methods
326
+ There is no analogous concern with #insert and its alias #[]=. These methods
328
327
  always write to the file system, but they also leave the object in the cache.
329
328
 
330
329
  - Performance is worse on Windows. Most of the delay seems to be in system,
331
330
  rather than user, code.
332
331
 
333
- == Advantages
332
+ ## Advantages
334
333
 
335
334
  - FSDB is useful with heterogeneous data, that is, with files in varying
336
335
  formats that can be recognized based on file name.
@@ -339,13 +338,15 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
339
338
  types. By defining new format clases, it's easy to set up databases that
340
339
  allow:
341
340
 
341
+ ``` ruby
342
342
  home['.forward'] += ["nobody@nowhere.net"]
343
343
  etc.edit('passwd') { |passwd| passwd['fred'].shell = '/bin/zsh' }
344
344
  window.setIcon(icons['apps/editor.png'])
345
-
345
+ ```
346
+
346
347
  - A FSDB can be operated on with ordinary file tools. FSDB can even treat
347
348
  existing file hierarchies as databases. It's easy to backup, export, grep,
348
- ... the database. Its just files.
349
+ rsync, tar, ... the database. Its just files.
349
350
 
350
351
  - FSDB is process-safe, so it can be used for *persistent*, *fault-tolerant*
351
352
  interprocess communication, such as a queue that doesn't require both
@@ -382,7 +383,7 @@ FSDB is not very fast. It's useful more for its safety, flexibility, and ease of
382
383
  - Pure ruby. Ruby license. Free software.
383
384
 
384
385
 
385
- == Applications
386
+ ## Applications
386
387
 
387
388
  I've heard from a couple of people writing applications that use FSDB. One app
388
389
  is:
@@ -390,9 +391,9 @@ is:
390
391
  - http://tourneybot.rubyforge.org
391
392
 
392
393
 
393
- == To do
394
+ ## To do
394
395
 
395
- === Fix (potential) bugs
396
+ ### Fix (potential) bugs
396
397
 
397
398
  - If two FSDBs are in use in the same process, they share the cache. If they
398
399
  associate different formats with the same file, the results will be
@@ -401,7 +402,7 @@ is:
401
402
  Database attributes, like lock-type (which should probably be global).
402
403
 
403
404
 
404
- === Features
405
+ ### Features
405
406
 
406
407
  - Should the Format objects be classes instead of just instances of Format?
407
408
 
@@ -409,21 +410,25 @@ is:
409
410
 
410
411
  - FSDB::Reference class:
411
412
 
413
+ ``` ruby
412
414
  db['foo/bar.obj'] = "some string"
413
415
  referrer = { :my_bar => FSDB::Reference.new('../foo/bar.obj') }
414
416
  db['x/y.yml'] = referrer
415
417
  p db['x/y.yml'][:my_bar] # ==> "some string"
418
+ ```
416
419
 
417
- Or, more like DRbUndumped:
420
+ Or, more like DRbUndumped:
418
421
 
422
+ ``` ruby
419
423
  str = "some string"
420
424
  str.extend FSDB::Undumped
421
425
  db['foo/bar.obj'] = str
422
426
  referrer = { :my_bar => str }
423
427
  db['x/y.yml'] = referrer
424
428
  p db['x/y.yml'][:my_bar] # ==> "some string"
429
+ ```
425
430
 
426
- Extending with FSDB::Undumped will have to insert state in the object that
431
+ Extending with FSDB::Undumped will have to insert state in the object that
427
432
  remembers the db path at which it is stored ('foo/bar.obj' in this case).
428
433
 
429
434
  - Use (optionally) weak references in CacheEntry.
@@ -439,20 +444,25 @@ is:
439
444
 
440
445
  - for edit and browse, but not replace or insert. Maybe delete.
441
446
 
442
- - db.edit [path1, path2] do |obj1, obj2| ... end
447
+ - `db.edit [path1, path2] do |obj1, obj2| ... end`
443
448
 
444
449
  - lock order is explicit, up to user to avoid deadlock
445
450
 
446
- - db.edit_glob "foo/**/bar*/{zap,zow}" ... do |hash|
451
+ - and:
452
+
453
+ ``` ruby
454
+ db.edit_glob "foo/**/bar*/{zap,zow}" ... do |hash|
447
455
  for path, object in hash ... end
448
456
  end
457
+ ```
449
458
 
450
459
  - Make irb-based database shell
451
460
 
452
- - class Database; def irb_browse(path); browse(path) {|obj| irb obj}; end; end
461
+ - `class Database; def irb_browse(path); browse(path) {|obj| irb obj}; end; end`
453
462
 
454
463
  then:
455
464
 
465
+ ```
456
466
  irb> irb db
457
467
  irb#1> irb_browse path
458
468
  ...
@@ -460,6 +470,7 @@ is:
460
470
  ...
461
471
  irb#1> ^D
462
472
  irb>
473
+ ```
463
474
 
464
475
  one problem: irb defines singleton methods, so can't dump (in edit)
465
476
 
@@ -489,7 +500,7 @@ is:
489
500
  - access control lists (use meta files)
490
501
 
491
502
 
492
- === Stability, Security, and Error Checking
503
+ ### Stability, Security, and Error Checking
493
504
 
494
505
  - investigate using the BDB lock mechanism in place of flock.
495
506
 
@@ -508,7 +519,7 @@ is:
508
519
  - should we detect recursive lock attempt and fail? (Now, it just deadlocks.)
509
520
 
510
521
 
511
- === Performance
522
+ ### Performance
512
523
 
513
524
  - Profiling says that Thread.exclusive consumes about 20% of cpu. Also,
514
525
  Thread.stop and Thread.run when there are multiple threads. Using
@@ -526,20 +537,20 @@ is:
526
537
 
527
538
  - Option for Database to ignore file locking and possibility of other writers.
528
539
 
529
- - #fetch could use the cache better if the cache kept the file contents string
540
+ - fetch could use the cache better if the cache kept the file contents string
530
541
  as well as the loaded object. Then the #stale! call would only have to
531
542
  wipe the reference to the object, and could leave the contents string. But
532
543
  this would increase file size and duplicate the file system's own cache.
533
544
 
534
- == Web site
545
+ ## Web site
535
546
 
536
- The current version of this software can be found at http://rubyforge.org/projects/fsdb.
547
+ The current version of this software can be found at http://rubyforge.org/projects/fsdb. The main git repo is at https://github.com/vjoel/fsdb.
537
548
 
538
- == License
549
+ ## License
539
550
 
540
551
  This software is distributed under the Ruby license. See http://www.ruby-lang.org.
541
552
 
542
- == Author
553
+ ## Author
543
554
 
544
555
  Joel VanderWerf, mailto:vjoel@users.sourceforge.net
545
- Copyright (c) 2003-2009, Joel VanderWerf.
556
+ Copyright (c) 2003-2011, Joel VanderWerf.