mdbx 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebac91a21c1f1c16214f94cead64d97c1773fb0689cdb6a4a0487bc3dbf12f63
4
- data.tar.gz: 6e527eeb9bd430742c1057699653f50c9c9437a3b4770a9213dfb0a3c7102eb1
3
+ metadata.gz: 97a83bdd83e99ab3b58cf823a914c8a7b75b5b63e33e13cd00f135aa2c8988b4
4
+ data.tar.gz: aa6687110e91dfe083332fc93299c492ce5628ad41fe26cb36d9ee33531e1039
5
5
  SHA512:
6
- metadata.gz: 9159f1033372110bb223b3dcfbbfce48e167da861cb078493d21293f85978c3ecbf744f02dbf6e4833aedb5669897c6d13f8662bf4a5a6d8ac05f5495643885e
7
- data.tar.gz: e3c23310cf9e05e51f5c0a21dbc50ca7bb60219b776c3745e0f527ec4df201927298e627fed6ab49871bdd4c734ea19810160a317540101d102460bd82392411
6
+ metadata.gz: 732ecf85ef600ae8edb433b18d67ecf26fbc1332fc673e7e01b20637dbcffff94735a8c1d79cc43c9d293cdda7b7532670f9a0c9f101234367b19aaac03149a9
7
+ data.tar.gz: 1623ff8c9d4b6e7752ef9faaf4075961119d812c7dfd78bb619fa74eaacb3e7fd1042b66b630e5638010478f61a0f3f232fa6cf2e1a37e40ef3d010a5c3257f6
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/History.md CHANGED
@@ -1,6 +1,29 @@
1
1
  # Release History for MDBX
2
2
 
3
3
  ---
4
+ ## v0.3.0 [2021-04-09] Mahlon E. Smith <mahlon@martini.nu>
5
+
6
+ Enhancements:
7
+
8
+ - Alter the behavior of #clear, so it doesn't destroy collection
9
+ environments, but just empties them.
10
+
11
+ - Add #drop, which explictly -does- destroy a collection environment.
12
+
13
+ - Switching to a collection now automatically creates its environment.
14
+
15
+ - Add include? and has_key?, for presence checks without allocating
16
+ value memory or requiring deserialization.
17
+
18
+
19
+ Bugfixes:
20
+
21
+ - Run all cursor methods through rb_protect, to ensure proper
22
+ cursor cleanup in the event of an exception mid iteration.
23
+
24
+ - Fix the block form of collections to support multiple scopes.
25
+
26
+
4
27
  ## v0.2.1 [2021-04-06] Mahlon E. Smith <mahlon@martini.nu>
5
28
 
6
29
  Enhancement:
@@ -6,7 +6,10 @@
6
6
  */
7
7
  #define UNWRAP_DB( val, db ) \
8
8
  rmdbx_db_t *db; \
9
- TypedData_Get_Struct( val, rmdbx_db_t, &rmdbx_db_data, db );
9
+ TypedData_Get_Struct( val, rmdbx_db_t, &rmdbx_db_data, db )
10
+
11
+ #define CHECK_HANDLE \
12
+ if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." )
10
13
 
11
14
 
12
15
  VALUE rmdbx_cDatabase;
@@ -160,7 +163,7 @@ rmdbx_open_env( VALUE self )
160
163
  void
161
164
  rmdbx_open_cursor( rmdbx_db_t *db )
162
165
  {
163
- if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
166
+ CHECK_HANDLE;
164
167
  if ( ! db->txn ) rb_raise( rmdbx_eDatabaseError, "No snapshot or transaction currently open." );
165
168
 
166
169
  int rc = mdbx_cursor_open( db->txn, db->dbi, &db->cursor );
@@ -215,11 +218,11 @@ rmdbx_close_txn( rmdbx_db_t *db, int txnflag )
215
218
  {
216
219
  if ( ! db->txn || db->state.retain_txn > -1 ) return;
217
220
 
218
- switch ( txnflag ) {
219
- case RMDBX_TXN_COMMIT:
220
- mdbx_txn_commit( db->txn );
221
- default:
222
- mdbx_txn_abort( db->txn );
221
+ if ( txnflag == RMDBX_TXN_COMMIT ) {
222
+ mdbx_txn_commit( db->txn );
223
+ }
224
+ else {
225
+ mdbx_txn_abort( db->txn );
223
226
  }
224
227
 
225
228
  db->txn = 0;
@@ -239,6 +242,7 @@ VALUE
239
242
  rmdbx_rb_opentxn( VALUE self, VALUE mode )
240
243
  {
241
244
  UNWRAP_DB( self, db );
245
+ CHECK_HANDLE;
242
246
 
243
247
  rmdbx_open_txn( db, RTEST(mode) ? MDBX_TXN_READWRITE : MDBX_TXN_RDONLY );
244
248
  db->state.retain_txn = RTEST(mode) ? 1 : 0;
@@ -273,13 +277,53 @@ rmdbx_rb_closetxn( VALUE self, VALUE write )
273
277
  *
274
278
  * Empty the current collection on disk. If collections are not enabled
275
279
  * or the database handle is set to the top-level (main) db - this
276
- * deletes *all records* from the database. This is not recoverable!
280
+ * deletes *all records* from the database.
277
281
  */
278
282
  VALUE
279
283
  rmdbx_clear( VALUE self )
280
284
  {
281
285
  UNWRAP_DB( self, db );
282
286
 
287
+ rmdbx_open_txn( db, MDBX_TXN_READWRITE );
288
+ int rc = mdbx_drop( db->txn, db->dbi, false );
289
+
290
+ if ( rc != MDBX_SUCCESS )
291
+ rb_raise( rmdbx_eDatabaseError, "mdbx_drop: (%d) %s", rc, mdbx_strerror(rc) );
292
+
293
+ rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
294
+
295
+ return Qnil;
296
+ }
297
+
298
+
299
+ /*
300
+ * call-seq:
301
+ * db.drop( collection ) -> db
302
+ *
303
+ * Destroy a collection. You must be in the top level database to call
304
+ * this method.
305
+ */
306
+ VALUE
307
+ rmdbx_drop( VALUE self, VALUE name )
308
+ {
309
+ UNWRAP_DB( self, db );
310
+
311
+ /* Provide a friendlier error message if max_collections is 0. */
312
+ if ( db->settings.max_collections == 0 )
313
+ rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: collections are not enabled." );
314
+
315
+ /* All transactions must be closed when dropping a database. */
316
+ if ( db->txn )
317
+ rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: transaction open" );
318
+
319
+ /* A drop can only be performed from the top-level database. */
320
+ if ( db->subdb != NULL )
321
+ rb_raise( rmdbx_eDatabaseError, "Unable to drop collection: switch to top-level db first" );
322
+
323
+ name = rb_funcall( name, rb_intern("to_s"), 0 );
324
+ db->subdb = StringValueCStr( name );
325
+
326
+ rmdbx_close_dbi( db ); /* ensure we're reopening within the new subdb */
283
327
  rmdbx_open_txn( db, MDBX_TXN_READWRITE );
284
328
  int rc = mdbx_drop( db->txn, db->dbi, true );
285
329
 
@@ -288,10 +332,11 @@ rmdbx_clear( VALUE self )
288
332
 
289
333
  rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
290
334
 
291
- /* Refresh the environment handles. */
292
- rmdbx_open_env( self );
335
+ /* Reset the current collection to the top level. */
336
+ db->subdb = NULL;
337
+ rmdbx_close_dbi( db ); /* ensure next access is not in the defunct subdb */
293
338
 
294
- return Qnil;
339
+ return self;
295
340
  }
296
341
 
297
342
 
@@ -348,21 +393,15 @@ rmdbx_deserialize( VALUE self, VALUE val )
348
393
  }
349
394
 
350
395
 
351
- /* call-seq:
352
- * db.each_key {|key| block } => self
353
- *
354
- * Calls the block once for each key, returning self.
355
- * A transaction must be opened prior to use.
396
+ /*
397
+ * Enumerate over keys for the current collection.
356
398
  */
357
399
  VALUE
358
- rmdbx_each_key( VALUE self )
400
+ rmdbx_each_key_i( VALUE self )
359
401
  {
360
402
  UNWRAP_DB( self, db );
361
403
  MDBX_val key, data;
362
404
 
363
- rmdbx_open_cursor( db );
364
- RETURN_ENUMERATOR( self, 0, 0 );
365
-
366
405
  if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
367
406
  rb_yield( rb_str_new( key.iov_base, key.iov_len ) );
368
407
  while ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_NEXT ) == MDBX_SUCCESS ) {
@@ -370,27 +409,45 @@ rmdbx_each_key( VALUE self )
370
409
  }
371
410
  }
372
411
 
373
- mdbx_cursor_close( db->cursor );
374
- db->cursor = NULL;
375
412
  return self;
376
413
  }
377
414
 
378
415
 
379
416
  /* call-seq:
380
- * db.each_value {|value| block } => self
417
+ * db.each_key {|key| block } => self
381
418
  *
382
- * Calls the block once for each value, returning self.
419
+ * Calls the block once for each key, returning self.
383
420
  * A transaction must be opened prior to use.
384
421
  */
385
422
  VALUE
386
- rmdbx_each_value( VALUE self )
423
+ rmdbx_each_key( VALUE self )
387
424
  {
388
425
  UNWRAP_DB( self, db );
389
- MDBX_val key, data;
426
+ int state;
390
427
 
428
+ CHECK_HANDLE;
391
429
  rmdbx_open_cursor( db );
392
430
  RETURN_ENUMERATOR( self, 0, 0 );
393
431
 
432
+ rb_protect( rmdbx_each_key_i, self, &state );
433
+
434
+ mdbx_cursor_close( db->cursor );
435
+ db->cursor = NULL;
436
+
437
+ if ( state ) rb_jump_tag( state );
438
+
439
+ return self;
440
+ }
441
+
442
+
443
+ /* Enumerate over values for the current collection.
444
+ */
445
+ VALUE
446
+ rmdbx_each_value_i( VALUE self )
447
+ {
448
+ UNWRAP_DB( self, db );
449
+ MDBX_val key, data;
450
+
394
451
  if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
395
452
  VALUE rv = rb_str_new( data.iov_base, data.iov_len );
396
453
  rb_yield( rmdbx_deserialize( self, rv ) );
@@ -401,27 +458,45 @@ rmdbx_each_value( VALUE self )
401
458
  }
402
459
  }
403
460
 
404
- mdbx_cursor_close( db->cursor );
405
- db->cursor = NULL;
406
461
  return self;
407
462
  }
408
463
 
409
464
 
410
465
  /* call-seq:
411
- * db.each_pair {|key, value| block } => self
466
+ * db.each_value {|value| block } => self
412
467
  *
413
- * Calls the block once for each key and value, returning self.
468
+ * Calls the block once for each value, returning self.
414
469
  * A transaction must be opened prior to use.
415
470
  */
416
471
  VALUE
417
- rmdbx_each_pair( VALUE self )
472
+ rmdbx_each_value( VALUE self )
418
473
  {
419
474
  UNWRAP_DB( self, db );
420
- MDBX_val key, data;
475
+ int state;
421
476
 
477
+ CHECK_HANDLE;
422
478
  rmdbx_open_cursor( db );
423
479
  RETURN_ENUMERATOR( self, 0, 0 );
424
480
 
481
+ rb_protect( rmdbx_each_value_i, self, &state );
482
+
483
+ mdbx_cursor_close( db->cursor );
484
+ db->cursor = NULL;
485
+
486
+ if ( state ) rb_jump_tag( state );
487
+
488
+ return self;
489
+ }
490
+
491
+
492
+ /* Enumerate over key and value pairs for the current collection.
493
+ */
494
+ VALUE
495
+ rmdbx_each_pair_i( VALUE self )
496
+ {
497
+ UNWRAP_DB( self, db );
498
+ MDBX_val key, data;
499
+
425
500
  if ( mdbx_cursor_get( db->cursor, &key, &data, MDBX_FIRST ) == MDBX_SUCCESS ) {
426
501
  VALUE rkey = rb_str_new( key.iov_base, key.iov_len );
427
502
  VALUE rval = rb_str_new( data.iov_base, data.iov_len );
@@ -434,12 +509,38 @@ rmdbx_each_pair( VALUE self )
434
509
  }
435
510
  }
436
511
 
512
+ return self;
513
+ }
514
+
515
+
516
+ /* call-seq:
517
+ * db.each_pair {|key, value| block } => self
518
+ *
519
+ * Calls the block once for each key and value, returning self.
520
+ * A transaction must be opened prior to use.
521
+ */
522
+ VALUE
523
+ rmdbx_each_pair( VALUE self )
524
+ {
525
+ UNWRAP_DB( self, db );
526
+ int state;
527
+
528
+ CHECK_HANDLE;
529
+ rmdbx_open_cursor( db );
530
+ RETURN_ENUMERATOR( self, 0, 0 );
531
+
532
+ rb_protect( rmdbx_each_pair_i, self, &state );
533
+
437
534
  mdbx_cursor_close( db->cursor );
438
535
  db->cursor = NULL;
536
+
537
+ if ( state ) rb_jump_tag( state );
538
+
439
539
  return self;
440
540
  }
441
541
 
442
542
 
543
+
443
544
  /* call-seq:
444
545
  * db.length -> Integer
445
546
  *
@@ -451,7 +552,7 @@ rmdbx_length( VALUE self )
451
552
  UNWRAP_DB( self, db );
452
553
  MDBX_stat mstat;
453
554
 
454
- if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
555
+ CHECK_HANDLE;
455
556
  rmdbx_open_txn( db, MDBX_TXN_RDONLY );
456
557
 
457
558
  int rc = mdbx_dbi_stat( db->txn, db->dbi, &mstat, sizeof(mstat) );
@@ -465,6 +566,39 @@ rmdbx_length( VALUE self )
465
566
  }
466
567
 
467
568
 
569
+ /* call-seq:
570
+ * db.include?( 'key' ) => bool
571
+ *
572
+ * Returns true if the current collection contains +key+.
573
+ */
574
+ VALUE
575
+ rmdbx_include( VALUE self, VALUE key )
576
+ {
577
+ int rc;
578
+ UNWRAP_DB( self, db );
579
+
580
+ CHECK_HANDLE;
581
+ rmdbx_open_txn( db, MDBX_TXN_RDONLY );
582
+
583
+ MDBX_val ckey = rmdbx_key_for( key );
584
+ MDBX_val data;
585
+ rc = mdbx_get( db->txn, db->dbi, &ckey, &data );
586
+ rmdbx_close_txn( db, RMDBX_TXN_ROLLBACK );
587
+
588
+ switch ( rc ) {
589
+ case MDBX_SUCCESS:
590
+ return Qtrue;
591
+
592
+ case MDBX_NOTFOUND:
593
+ return Qfalse;
594
+
595
+ default:
596
+ rmdbx_close( self );
597
+ rb_raise( rmdbx_eDatabaseError, "Unable to fetch key: (%d) %s", rc, mdbx_strerror(rc) );
598
+ }
599
+ }
600
+
601
+
468
602
  /* call-seq:
469
603
  * db[ 'key' ] => value
470
604
  *
@@ -476,7 +610,7 @@ rmdbx_get_val( VALUE self, VALUE key )
476
610
  int rc;
477
611
  UNWRAP_DB( self, db );
478
612
 
479
- if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
613
+ CHECK_HANDLE;
480
614
  rmdbx_open_txn( db, MDBX_TXN_RDONLY );
481
615
 
482
616
  MDBX_val ckey = rmdbx_key_for( key );
@@ -503,7 +637,8 @@ rmdbx_get_val( VALUE self, VALUE key )
503
637
  /* call-seq:
504
638
  * db[ 'key' ] = value
505
639
  *
506
- * Set a single value for +key+.
640
+ * Set a single value for +key+. If the value is +nil+, the
641
+ * key is removed.
507
642
  */
508
643
  VALUE
509
644
  rmdbx_put_val( VALUE self, VALUE key, VALUE val )
@@ -511,7 +646,7 @@ rmdbx_put_val( VALUE self, VALUE key, VALUE val )
511
646
  int rc;
512
647
  UNWRAP_DB( self, db );
513
648
 
514
- if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
649
+ CHECK_HANDLE;
515
650
  rmdbx_open_txn( db, MDBX_TXN_READWRITE );
516
651
 
517
652
  MDBX_val ckey = rmdbx_key_for( key );
@@ -552,41 +687,32 @@ VALUE
552
687
  rmdbx_stats( VALUE self )
553
688
  {
554
689
  UNWRAP_DB( self, db );
555
- if ( ! db->state.open ) rb_raise( rmdbx_eDatabaseError, "Closed database." );
690
+ CHECK_HANDLE;
556
691
 
557
692
  return rmdbx_gather_stats( db );
558
693
  }
559
694
 
560
695
 
561
696
  /*
562
- * call-seq:
563
- * db.collection -> (collection name, or nil if in main)
564
- * db.collection( 'collection_name' ) -> db
565
- * db.collection( nil ) -> db (main)
566
- *
567
- * Gets or sets the sub-database "collection" that read/write
568
- * operations apply to.
569
- * Passing +nil+ sets the database to the main, top-level namespace.
570
- * If a block is passed, the collection automatically reverts to the
571
- * prior collection when it exits.
572
- *
573
- * db.collection( 'collection_name' ) do
574
- * [ ... ]
575
- * end # reverts to the previous collection name
576
- *
697
+ * Return the currently selected collection, or +nil+ if at the
698
+ * top-level.
577
699
  */
578
700
  VALUE
579
- rmdbx_set_subdb( int argc, VALUE *argv, VALUE self )
701
+ rmdbx_get_subdb( VALUE self )
580
702
  {
581
703
  UNWRAP_DB( self, db );
582
- VALUE subdb, block;
583
- char *prev_db = NULL;
704
+ return ( db->subdb == NULL ) ? Qnil : rb_str_new_cstr( db->subdb );
705
+ }
584
706
 
585
- rb_scan_args( argc, argv, "01&", &subdb, &block );
586
- if ( argc == 0 ) {
587
- if ( db->subdb == NULL ) return Qnil;
588
- return rb_str_new_cstr( db->subdb );
589
- }
707
+
708
+ /*
709
+ * Sets the current collection name for read/write operations.
710
+ *
711
+ */
712
+ VALUE
713
+ rmdbx_set_subdb( VALUE self, VALUE name )
714
+ {
715
+ UNWRAP_DB( self, db );
590
716
 
591
717
  /* Provide a friendlier error message if max_collections is 0. */
592
718
  if ( db->settings.max_collections == 0 )
@@ -596,38 +722,14 @@ rmdbx_set_subdb( int argc, VALUE *argv, VALUE self )
596
722
  if ( db->txn )
597
723
  rb_raise( rmdbx_eDatabaseError, "Unable to change collection: transaction open" );
598
724
 
599
- /* Retain the prior database collection if a block was passed.
600
- */
601
- if ( rb_block_given_p() && db->subdb != NULL ) {
602
- prev_db = (char *) malloc( strlen(db->subdb) + 1 );
603
- strcpy( prev_db, db->subdb );
604
- }
725
+ db->subdb = NIL_P( name ) ? NULL : StringValueCStr( name );
605
726
 
606
- if ( NIL_P(subdb) ) {
607
- db->subdb = NULL;
608
- }
609
- else {
610
- subdb = rb_funcall( subdb, rb_intern("to_s"), 0 );
611
- db->subdb = StringValueCStr( subdb );
612
- }
727
+ /* Reset the db handle and issue a single transaction to reify
728
+ the collection.
729
+ */
613
730
  rmdbx_close_dbi( db );
614
-
615
- /*
616
- FIXME: Immediate transaction write to auto-create new env?
617
- Fetching from here at the moment causes an error if you
618
- haven't written anything to the new collection yet.
619
- */
620
-
621
- /* Revert to the previous collection after the block is done.
622
- */
623
- if ( rb_block_given_p() ) {
624
- rb_yield( self );
625
- if ( db->subdb != prev_db ) {
626
- db->subdb = prev_db;
627
- rmdbx_close_dbi( db );
628
- }
629
- xfree( prev_db );
630
- }
731
+ rmdbx_open_txn( db, MDBX_TXN_READWRITE );
732
+ rmdbx_close_txn( db, RMDBX_TXN_COMMIT );
631
733
 
632
734
  return self;
633
735
  }
@@ -767,16 +869,18 @@ rmdbx_init_database()
767
869
 
768
870
  rb_define_protected_method( rmdbx_cDatabase, "initialize", rmdbx_database_initialize, -1 );
769
871
  rb_define_protected_method( rmdbx_cDatabase, "initialize_copy", rmdbx_init_copy, 1 );
770
- rb_define_method( rmdbx_cDatabase, "collection", rmdbx_set_subdb, -1 );
872
+
873
+ rb_define_method( rmdbx_cDatabase, "clear", rmdbx_clear, 0 );
771
874
  rb_define_method( rmdbx_cDatabase, "close", rmdbx_close, 0 );
772
- rb_define_method( rmdbx_cDatabase, "reopen", rmdbx_open_env, 0 );
773
875
  rb_define_method( rmdbx_cDatabase, "closed?", rmdbx_closed_p, 0 );
774
- rb_define_method( rmdbx_cDatabase, "in_transaction?", rmdbx_in_transaction_p, 0 );
775
- rb_define_method( rmdbx_cDatabase, "clear", rmdbx_clear, 0 );
876
+ rb_define_method( rmdbx_cDatabase, "drop", rmdbx_drop, 1 );
776
877
  rb_define_method( rmdbx_cDatabase, "each_key", rmdbx_each_key, 0 );
777
- rb_define_method( rmdbx_cDatabase, "each_value", rmdbx_each_value, 0 );
778
878
  rb_define_method( rmdbx_cDatabase, "each_pair", rmdbx_each_pair, 0 );
879
+ rb_define_method( rmdbx_cDatabase, "each_value", rmdbx_each_value, 0 );
880
+ rb_define_method( rmdbx_cDatabase, "in_transaction?", rmdbx_in_transaction_p, 0 );
881
+ rb_define_method( rmdbx_cDatabase, "include?", rmdbx_include, 1 );
779
882
  rb_define_method( rmdbx_cDatabase, "length", rmdbx_length, 0 );
883
+ rb_define_method( rmdbx_cDatabase, "reopen", rmdbx_open_env, 0 );
780
884
  rb_define_method( rmdbx_cDatabase, "[]", rmdbx_get_val, 1 );
781
885
  rb_define_method( rmdbx_cDatabase, "[]=", rmdbx_put_val, 2 );
782
886
 
@@ -784,6 +888,10 @@ rmdbx_init_database()
784
888
  rb_define_protected_method( rmdbx_cDatabase, "open_transaction", rmdbx_rb_opentxn, 1 );
785
889
  rb_define_protected_method( rmdbx_cDatabase, "close_transaction", rmdbx_rb_closetxn, 1 );
786
890
 
891
+ /* Collection functions */
892
+ rb_define_protected_method( rmdbx_cDatabase, "get_subdb", rmdbx_get_subdb, 0 );
893
+ rb_define_protected_method( rmdbx_cDatabase, "set_subdb", rmdbx_set_subdb, 1 );
894
+
787
895
  rb_define_protected_method( rmdbx_cDatabase, "raw_stats", rmdbx_stats, 0 );
788
896
 
789
897
  rb_require( "mdbx/database" );
data/lib/mdbx.rb CHANGED
@@ -10,7 +10,7 @@ require 'mdbx_ext'
10
10
  module MDBX
11
11
 
12
12
  # The version of this gem.
13
- VERSION = '0.2.1'
13
+ VERSION = '0.3.1'
14
14
 
15
15
  end # module MDBX
16
16
 
data/lib/mdbx/database.rb CHANGED
@@ -128,16 +128,43 @@ class MDBX::Database
128
128
  attr_accessor :deserializer
129
129
 
130
130
 
131
+ alias_method :size, :length
132
+ alias_method :each, :each_pair
133
+ alias_method :has_key?, :include?
134
+
135
+
136
+ ### Gets or sets the sub-database "collection" that read/write
137
+ ### operations apply to. If a block is passed, the collection
138
+ ### automatically reverts to the prior collection when it exits.
139
+ ###
140
+ ### db.collection #=> (collection name, or nil if in main)
141
+ ### db.collection( 'collection_name' ) #=> db
142
+ ###
143
+ ### db.collection( 'collection_name' ) do
144
+ ### [ ... ]
145
+ ### end # reverts to the previous collection name
146
+ ###
147
+ def collection( name=nil )
148
+ current = self.get_subdb
149
+ return current unless name
150
+
151
+ self.set_subdb( name.to_s )
152
+ yield( self ) if block_given?
153
+
154
+ return self
155
+
156
+ ensure
157
+ self.set_subdb( current ) if name && block_given?
158
+ end
159
+ alias_method :namespace, :collection
160
+
161
+
131
162
  ### Switch to the top-level collection.
132
163
  ###
133
164
  def main
134
- return self.collection( nil )
165
+ return self.set_subdb( nil )
135
166
  end
136
167
 
137
- alias_method :namespace, :collection
138
- alias_method :size, :length
139
- alias_method :each, :each_pair
140
-
141
168
 
142
169
  #
143
170
  # Transaction methods
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mdbx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mahlon E. Smith
@@ -34,7 +34,7 @@ cert_chain:
34
34
  49pOzX5KHZLTS9DKeaP/xcGPz6C8MiwQdYrZarr2SHRASX1zFa79rkItO8kE6RDr
35
35
  b6WDF79UvZ55ajtE00TiwqjQL/ZPEtbd
36
36
  -----END CERTIFICATE-----
37
- date: 2021-04-06 00:00:00.000000000 Z
37
+ date: 2021-05-16 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: pry
metadata.gz.sig CHANGED
Binary file