lmdb 0.5.3 → 0.6

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: c02c1c99b143e331bf5fdbae711a57c4845b32ca07c4c041c9c639b96cbb8388
4
- data.tar.gz: ce305105acf85f4161e654dc80ad045baeded4e8a8e39f45fed9f8da68f5c2ce
3
+ metadata.gz: 2eac165bb0261c0af6813f16df9b725b2ca2a63f4c5de98734473d9976930735
4
+ data.tar.gz: 3aa0562060e4586dd7c7a44e795adc1449f489f57634787ef5e6480abbd49a48
5
5
  SHA512:
6
- metadata.gz: 1303411d1515bf39a6406540f3a10d9e12c2bd3fea597d17a9e2b949bcf7707244381467a624edd15fada41ac65ae87721c2e42a9ec4dadb5b37e9f8db2a55b0
7
- data.tar.gz: aeaa5861dcbde41e9fec9634f6cae749dce6a1348ab9541f90247c2cff60dcb10180701348757a00ba7fc11e93431ef97cfb3064e5face3cc50c704498ca183d
6
+ metadata.gz: 22cc9f78002a05aa25fba212e4d3e3e97566ed7a9bd2e0c84996297398131b5afd57da0e1510a27086ac4b1938401d1369393f9b0515bdd044856109510c6432
7
+ data.tar.gz: eea983ccb0638bed2dbcfcd3e2ea76271072ea91d259b95e392d505c26f184c351f89ae6076025b93f47bfec3e42add3ec41055e5edb67bee5bfec2e8e341a80
data/.gitignore CHANGED
@@ -11,3 +11,5 @@ doc/
11
11
  .yardoc/
12
12
  *.gem
13
13
  dtrace/
14
+ \#*\#
15
+ .\#*
data/CONTRIBUTORS CHANGED
@@ -5,3 +5,4 @@ Julien Ammous <schmurfy@gmail.com>
5
5
  Nathaniel Pierce <nwpierce@gmail.com>
6
6
  Richard Golding <golding@chrysaetos.org>
7
7
  Joel VanderWerf <vjoel@users.sourceforge.net>
8
+ Dorian Taylor <code@doriantaylor.com>
data/Rakefile CHANGED
@@ -7,12 +7,21 @@ PRJ = File.basename(GEMSPEC, ".gemspec")
7
7
  require 'bundler/setup'
8
8
  require 'rspec/core/rake_task'
9
9
  require 'rake/extensiontask'
10
+ require 'ruby_memcheck'
11
+ require 'ruby_memcheck/rspec/rake_task'
12
+
13
+ RubyMemcheck.config(binary_name: 'lmdb_ext')
10
14
 
11
15
  RSpec::Core::RakeTask.new :spec
12
16
  Rake::ExtensionTask.new :lmdb_ext
13
17
 
18
+
14
19
  task :default => [:compile, :spec]
15
20
 
21
+ namespace :spec do
22
+ RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
23
+ end
24
+
16
25
  def version
17
26
  @version ||= begin
18
27
  require "#{PRJ}/version"
data/behaviour.org ADDED
@@ -0,0 +1,35 @@
1
+ #+STARTUP: showall hidestars
2
+ * the situtation
3
+ - lmdb has two kinds of transaction: read-write and read-only
4
+ - there are two properties associated with transactions:
5
+ - nesting
6
+ - multiplicity
7
+ - *read-write* transactions can nest, but there can only be one stack of read-write transactions per *database*, that is, amongst /all/ threads and all processes attached to the persitent storage.
8
+ - *read-only* transactions /cannot/ nest (because it's not meaningful for a read-only transaction to nest), but there can be a read-only transaction per thread (and of course multiple threads per process).
9
+ - (i can't remember if you can have a thread with a read-write *and* read-only transaction going but we can probably assume not)
10
+ - so therefore we need a way to distinguish between read-write and read-only as well as identify the thread /across processes/ that has the one read-write transaction open.
11
+ * undocumented behaviour
12
+ ** ~mdb_txn_begin~
13
+ - if the environment is read-only and the transaction is read-write, returns ~EACCES~
14
+ - if there is a parent transaction and the current transaction's flags are ~MDB_RDONLY~ or ~MDB_WRITEMAP~ (?) or ~TXN_BLOCKED~
15
+ - if the *parent's* transaction is ~MDB_TXN_RDONLY~ (which is the same as ~MDB_RDONLY~), return ~EINVAL~
16
+ - that's saying "read-only transactions can't be nested"
17
+ - otherwise, return ~MDB_BAD_TXN~
18
+ - this is saying "read-only transactions can't be children of read-write parents"
19
+ - otherwise a few boring scenarios where the function may return ~ENOMEM~
20
+ - otherwise check ~mdb_cursor_shadow~ or ~mdb_txn_renew0~
21
+ - XXX does ~mdb_txn_begin~ block when waiting for the read-write??
22
+ * desired behaviour
23
+ ** ruby interface
24
+ - when the ruby programmer opens a read-only transaction within a read-only transaction, this should be a noop
25
+ - don't push any stack, don't allocate any resources, just do nothing
26
+ - when the ruby programmer opens a read-only transaction within a read-/write/ transaction, this should raise an exception
27
+ - in practice there's no harm except that this is more about communicating the right thing to the ruby programmer
28
+ - do we warn?
29
+ - problem: there's no way to know (via the lmdb api) if another process has a read-write transaction open
30
+ - poll?
31
+ - actually no it probably doesn't matter (the mdb api blocks anyway?)
32
+ - /actually/ actually, the cursed-ass goto pseudo-loop containing the ~CALL_WITHOUT_GVL~ deals with that
33
+ ** internal implementation
34
+ - a successfully-created read-write transaction has to set ~rw_txn_thread~ to the current thread (unless it is a sub-transaction in which case noop)
35
+ - when a read-write transaction is committed or aborted, ~rw_txn_thread~ has to be set back to null (unless the transaction has a parent)
@@ -1,6 +1,7 @@
1
1
  require 'mkmf'
2
2
 
3
3
  $CFLAGS = '-std=c99 -Wall -g'
4
+ $CFLAGS << ' -fdeclspec' if /darwin/.match? RUBY_PLATFORM
4
5
 
5
6
  # Embed lmdb if we cannot find it
6
7
  if enable_config("bundled-lmdb", false) || !(find_header('lmdb.h') && have_library('lmdb', 'mdb_env_create'))
@@ -26,6 +26,7 @@ static void check(int code) {
26
26
 
27
27
  const char* err = mdb_strerror(code);
28
28
  const char* sep = strchr(err, ':');
29
+ // increment the offset by two in case there is a colon (plus space)
29
30
  if (sep)
30
31
  err = sep + 2;
31
32
 
@@ -37,7 +38,7 @@ static void check(int code) {
37
38
  }
38
39
 
39
40
  static void transaction_free(Transaction* transaction) {
40
- if (transaction->txn) {
41
+ if (transaction->txn && !NIL_P(transaction->txn)) {
41
42
  //int id = (int)mdb_txn_id(transaction->txn);
42
43
  //rb_warn(sprintf("Memory leak: Garbage collecting active transaction %d", id));
43
44
  rb_warn("Memory leak: Garbage collecting active transaction");
@@ -49,6 +50,7 @@ static void transaction_free(Transaction* transaction) {
49
50
 
50
51
  static void transaction_mark(Transaction* transaction) {
51
52
  rb_gc_mark(transaction->parent);
53
+ rb_gc_mark(transaction->child);
52
54
  rb_gc_mark(transaction->env);
53
55
  rb_gc_mark(transaction->cursors);
54
56
  }
@@ -130,55 +132,79 @@ static VALUE transaction_env(VALUE self) {
130
132
  * @return [false,true] whether the transaction is read-only.
131
133
  */
132
134
  static VALUE transaction_is_readonly(VALUE self) {
133
- TRANSACTION(self, transaction);
134
- //MDB_txn* txn = transaction->txn;
135
- return (transaction->flags & MDB_RDONLY) ? Qtrue : Qfalse;
135
+ TRANSACTION(self, transaction);
136
+ //MDB_txn* txn = transaction->txn;
137
+ return (transaction->flags & MDB_RDONLY) ? Qtrue : Qfalse;
136
138
  }
137
139
 
138
140
 
139
141
  static void transaction_finish(VALUE self, int commit) {
140
- TRANSACTION(self, transaction);
141
-
142
- if (!transaction->txn)
143
- rb_raise(cError, "Transaction is terminated");
144
-
145
- if (transaction->thread != rb_thread_current())
146
- rb_raise(cError, "Wrong thread");
147
-
148
- // Check nesting
149
- VALUE p = environment_active_txn(transaction->env);
150
- while (!NIL_P(p) && p != self) {
151
- TRANSACTION(p, txn);
152
- p = txn->parent;
153
- }
154
- if (p != self)
155
- rb_raise(cError, "Transaction is not active");
156
-
157
- int ret = 0;
158
- if (commit)
159
- ret = mdb_txn_commit(transaction->txn);
160
- else
161
- mdb_txn_abort(transaction->txn);
162
-
163
- long i;
164
- for (i=0; i<RARRAY_LEN(transaction->cursors); i++) {
165
- VALUE cursor = RARRAY_AREF(transaction->cursors, i);
166
- cursor_close(cursor);
167
- }
168
- rb_ary_clear(transaction->cursors);
169
-
170
- // Mark child transactions as closed
171
- p = environment_active_txn(transaction->env);
142
+ TRANSACTION(self, transaction);
143
+
144
+ if (!transaction->txn)
145
+ rb_raise(cError, "Transaction is already terminated");
146
+
147
+ if (transaction->thread != rb_thread_current())
148
+ rb_raise(cError, "The thread closing the transaction "
149
+ "is not the one that opened it");
150
+
151
+ // ensure the transaction being closed is the active one
152
+ VALUE p = environment_active_txn(transaction->env);
153
+ while (!NIL_P(p) && p != self) {
154
+ TRANSACTION(p, txn);
155
+ p = txn->parent;
156
+ }
157
+ // bail out if the transaction `self` is not the active one
158
+ if (p != self)
159
+ rb_raise(cError, "Transaction is not active");
160
+
161
+ // now eliminate the cursors
162
+ long i;
163
+ for (i=0; i<RARRAY_LEN(transaction->cursors); i++) {
164
+ VALUE cursor = RARRAY_AREF(transaction->cursors, i);
165
+ cursor_close(cursor);
166
+ }
167
+ rb_ary_clear(transaction->cursors);
168
+
169
+ // now actually finish the internal transaction
170
+ int ret = 0;
171
+ if (commit)
172
+ ret = mdb_txn_commit(transaction->txn);
173
+ else
174
+ mdb_txn_abort(transaction->txn);
175
+
176
+ // eliminate child transactions
177
+ if (transaction->child) {
178
+ p = self; // again this is a VALUE
179
+ Transaction* txn = transaction; // and this is the struct
180
+
181
+ // descend into deepest child transaction
182
+ do {
183
+ p = txn->child;
184
+ // this is TRANSACTION minus the declaration
185
+ Data_Get_Struct(txn->child, Transaction, txn);
186
+ } while (txn->child);
187
+
188
+ // now we ascend back up
172
189
  while (p != self) {
173
- TRANSACTION(p, txn);
174
- txn->txn = 0;
175
- p = txn->parent;
190
+ TRANSACTION(p, txn);
191
+ txn->txn = 0;
192
+ p = txn->parent;
176
193
  }
177
- transaction->txn = 0;
194
+ }
195
+ transaction->txn = 0;
178
196
 
179
- environment_set_active_txn(transaction->env, transaction->thread, transaction->parent);
197
+ // no more active read-write transaction; unset the registry
198
+ if (!(transaction->flags & MDB_RDONLY) && !transaction->parent) {
199
+ ENVIRONMENT(transaction->env, env);
200
+ env->rw_txn_thread = NULL;
201
+ }
180
202
 
181
- check(ret);
203
+ // now set the active transaction to the parent, if there is one
204
+ environment_set_active_txn(transaction->env, transaction->thread,
205
+ transaction->parent);
206
+
207
+ check(ret);
182
208
  }
183
209
 
184
210
  // Ruby 1.8.7 compatibility
@@ -219,65 +245,143 @@ static void stop_txn_begin(void *arg)
219
245
  txn_args->stop = 1;
220
246
  }
221
247
 
248
+ /**
249
+ * This is the code that opens transactions. Read-write transactions
250
+ * have to be called outside the GVL because they will block without
251
+ *
252
+ *
253
+ * Here is the basic problem with LMDB transactions:
254
+ *
255
+ * - There can only be one read-write transaction per LMDB
256
+ * ENVIRONMENT, not process, not thread.
257
+ *
258
+ * - Read-write transactions can nest.
259
+ *
260
+ * - There can only be one active transaction per thread.
261
+ *
262
+ * - Every thread can have an active read-ONLY transaction, but only one.
263
+ *
264
+ * - This is because read-only transactions canNOT nest (it is
265
+ * meaningless to have a nested read-only transaction)
266
+ *
267
+ * - Furthermore it is an error to open a read-only transaction under
268
+ * a read-write transaction.
269
+ *
270
+ * - Same goes for opening a read-write transaction in the same thread
271
+ * as an active read-only transaction.
272
+ *
273
+ * Nevertheless, the downstream Ruby user may not be able to
274
+ * completely control calls to env.transaction (e.g. if they are
275
+ * wrapping a transaction around a proc or lambda that happens to
276
+ * contain its own transaction), plus every call in LMDB needs to be
277
+ * implicitly wrapped in a transaction anyway.
278
+ *
279
+ * What this means is that we will, first, have to keep explicit track
280
+ * of the read-write transaction, if one exists. Second, we will have
281
+ * to simulate nesting for read-only transactions, so the behaviour in
282
+ * Ruby is the same as a read-write transaction.
283
+ *
284
+ */
285
+
222
286
  static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags) {
223
- ENVIRONMENT(venv, environment);
287
+ ENVIRONMENT(venv, environment);
224
288
 
225
- MDB_txn* txn;
226
- TxnArgs txn_args;
289
+ MDB_txn* txn;
290
+ TxnArgs txn_args;
227
291
 
228
- retry:
229
- txn = NULL;
292
+ VALUE thread = rb_thread_current();
293
+ VALUE vparent = environment_active_txn(venv);
230
294
 
231
- txn_args.env = environment->env;
232
- txn_args.parent = active_txn(venv);
233
- txn_args.flags = flags;
234
- txn_args.htxn = &txn;
235
- txn_args.result = 0;
236
- txn_args.stop = 0;
295
+ Transaction* tparent = NULL;
296
+ if (vparent && !NIL_P(vparent))
297
+ Data_Get_Struct(vparent, Transaction, tparent);
237
298
 
238
- if (flags & MDB_RDONLY) {
239
- call_txn_begin(&txn_args);
240
- }
241
- else {
242
- CALL_WITHOUT_GVL(
243
- call_txn_begin, &txn_args,
244
- stop_txn_begin, &txn_args);
245
-
246
- if (txn_args.stop || !txn) {
247
- // !txn is when rb_thread_call_without_gvl2
248
- // returns before calling txn_begin
249
- if (txn) mdb_txn_abort(txn);
250
-
251
- //rb_warn("got here lol");
252
- rb_thread_check_ints();
253
- goto retry; // in what cases do we get here?
254
- }
299
+ // rb_warn("fart lol");
300
+
301
+ // XXX note this is a cursed goto loop that could almost certainly
302
+ // be rewritten as a do-while
303
+ retry:
304
+ txn = NULL;
305
+
306
+ txn_args.env = environment->env;
307
+ txn_args.parent = active_txn(venv);
308
+ txn_args.flags = flags;
309
+ txn_args.htxn = &txn;
310
+ txn_args.result = 0;
311
+ txn_args.stop = 0;
312
+
313
+ if (flags & MDB_RDONLY) {
314
+ if (tparent && tparent->flags & MDB_RDONLY)
315
+ // this is a no-op: put the same actual transaction in a
316
+ // different wrapper struct
317
+ txn = txn_args.parent;
318
+ else
319
+ // this will return an error if the parent transaction is
320
+ // read-write, so we don't need to handle the case explicitly
321
+ call_txn_begin(&txn_args);
322
+ }
323
+ else {
324
+ if (tparent) {
325
+ // first we have to determine if we're on the same thread
326
+ // as the parent, which in turn must be the same as the
327
+ // environment's registry for which thread has the
328
+ // read-write transaction
329
+ if (thread != tparent->thread ||
330
+ thread != environment->rw_txn_thread)
331
+ rb_raise(cError,
332
+ "Attempt to nest transaction on a different thread");
255
333
  }
256
334
 
257
- check(txn_args.result);
258
-
259
- Transaction* transaction;
260
- VALUE vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark, transaction_free, transaction);
261
- transaction->parent = environment_active_txn(venv);
262
- transaction->env = venv;
263
- transaction->txn = txn;
264
- transaction->flags = flags;
265
- transaction->thread = rb_thread_current();
266
- transaction->cursors = rb_ary_new();
267
- environment_set_active_txn(venv, transaction->thread, vtxn);
268
-
269
- int exception;
270
- VALUE ret = rb_protect(fn, NIL_P(arg) ? vtxn : arg, &exception);
271
-
272
- if (exception) {
273
- //rb_warn("lol got exception");
274
- if (vtxn == environment_active_txn(venv))
275
- transaction_abort(vtxn);
276
- rb_jump_tag(exception);
335
+ CALL_WITHOUT_GVL(call_txn_begin, &txn_args, stop_txn_begin, &txn_args);
336
+
337
+ if (txn_args.stop || !txn) {
338
+ // !txn is when rb_thread_call_without_gvl2
339
+ // returns before calling txn_begin
340
+ if (txn) {
341
+ mdb_txn_abort(txn);
342
+ txn_args.result = 0;
343
+ }
344
+
345
+ //rb_warn("got here lol");
346
+ rb_thread_check_ints();
347
+ goto retry; // in what cases do we get here?
277
348
  }
349
+
350
+ // set the thread
351
+ environment->rw_txn_thread = thread;
352
+ }
353
+
354
+ // this will raise unless result is zero
355
+ check(txn_args.result);
356
+
357
+ Transaction* transaction;
358
+ VALUE vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark,
359
+ transaction_free, transaction);
360
+ transaction->parent = vparent;
361
+ transaction->env = venv;
362
+ transaction->txn = txn;
363
+ transaction->flags = flags;
364
+ transaction->thread = rb_thread_current();
365
+ transaction->cursors = rb_ary_new();
366
+
367
+ // set the parent's child to self
368
+ if (tparent) tparent->child = vtxn;
369
+
370
+ environment_set_active_txn(venv, transaction->thread, vtxn);
371
+
372
+ // now we run the function in the transaction
373
+ int exception;
374
+ VALUE ret = rb_protect(fn, NIL_P(arg) ? vtxn : arg, &exception);
375
+
376
+ if (exception) {
377
+ // rb_warn("lol got exception");
278
378
  if (vtxn == environment_active_txn(venv))
279
- transaction_commit(vtxn);
280
- return ret;
379
+ transaction_abort(vtxn);
380
+ rb_jump_tag(exception);
381
+ }
382
+ if (vtxn == environment_active_txn(venv))
383
+ transaction_commit(vtxn);
384
+ return ret;
281
385
  }
282
386
 
283
387
  static void environment_check(Environment* environment) {
@@ -674,7 +778,7 @@ static MDB_txn* active_txn(VALUE self) {
674
778
  return 0;
675
779
  TRANSACTION(vtxn, transaction);
676
780
  if (!transaction->txn)
677
- rb_raise(cError, "Transaction is terminated");
781
+ rb_raise(cError, "Transaction is already terminated");
678
782
  if (transaction->thread != rb_thread_current())
679
783
  rb_raise(cError, "Wrong thread");
680
784
  return transaction->txn;
@@ -47,19 +47,35 @@
47
47
  Data_Get_Struct(var, Cursor, var_cur); \
48
48
  cursor_check(var_cur)
49
49
 
50
+ /*
51
+ hey yo if you can convince hyc to add a function like
52
+ mdb_txn_get_flags or even mdb_txn_is_rdonly, you could probably get
53
+ rid of these wrapper structs and a lot of complexity, cause that is
54
+ literally the only reason why they're needed
55
+
56
+ actually no we would also need to know when a txn has a child and
57
+ there is also no mdb_txn_get_child or whatever
58
+ */
59
+
50
60
  typedef struct {
51
61
  VALUE env;
52
- VALUE parent;
62
+ VALUE parent; // ignored for ro threads
63
+ VALUE child; // ditto
53
64
  VALUE thread;
54
65
  VALUE cursors;
55
66
  MDB_txn* txn;
56
67
  unsigned int flags;
57
68
  } Transaction;
58
69
 
70
+ // we have an extra field `rw_txn_thread` here that acts as the
71
+ // registry for the single read-write transaction. if it's populated,
72
+ // that's the one
73
+
59
74
  typedef struct {
60
75
  MDB_env* env;
61
- VALUE thread_txn_hash;
62
- VALUE txn_thread_hash;
76
+ VALUE thread_txn_hash; /* transaction -> thread */
77
+ VALUE txn_thread_hash; /* thread -> transaction */
78
+ VALUE rw_txn_thread; /* the thread with the rw transaction */
63
79
  } Environment;
64
80
 
65
81
  typedef struct {
data/lib/lmdb/database.rb CHANGED
@@ -11,8 +11,8 @@ module LMDB
11
11
  # puts "at #{key}: #{value}"
12
12
  # end
13
13
  def each
14
- # maybe_txn true do
15
- env.transaction do
14
+ maybe_txn true do
15
+ # env.transaction do
16
16
  cursor do |c|
17
17
  while i = c.next
18
18
  yield(i)
@@ -56,8 +56,8 @@ module LMDB
56
56
  # @return [Enumerator] in lieu of a block.
57
57
  def each_key(&block)
58
58
  return enum_for :each_key unless block_given?
59
- # maybe_txn true do
60
- env.transaction do
59
+ maybe_txn true do
60
+ #env.transaction do
61
61
  cursor do |c|
62
62
  while (rec = c.next true)
63
63
  yield rec.first
@@ -81,8 +81,8 @@ module LMDB
81
81
  return
82
82
  end
83
83
 
84
- #maybe_txn true do
85
- env.transaction do
84
+ maybe_txn true do
85
+ # env.transaction do
86
86
  cursor do |c|
87
87
  method = :set
88
88
  while rec = c.send(method, key)
@@ -124,8 +124,9 @@ module LMDB
124
124
 
125
125
  ret = false
126
126
  # read-only txn was having trouble being nested inside a read-write
127
- #maybe_txn true do
128
- env.transaction do
127
+ maybe_txn true do
128
+ # env.transaction true do
129
+ # env.transaction do
129
130
  cursor { |c| ret = !!c.set(key, value) }
130
131
  end
131
132
  ret
data/lib/lmdb/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LMDB
2
- VERSION = '0.5.3'.freeze
2
+ VERSION = '0.6'.freeze
3
3
  end
data/lmdb.gemspec CHANGED
@@ -23,4 +23,5 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency 'rake', "~> 13.0"
24
24
  s.add_development_dependency 'rake-compiler', '~> 1.1'
25
25
  s.add_development_dependency 'rspec', "~> 3.0"
26
+ s.add_development_dependency 'ruby_memcheck', "~> 1.0"
26
27
  end
data/spec/helper.rb CHANGED
@@ -2,6 +2,9 @@ require 'lmdb'
2
2
  require 'rspec'
3
3
  require 'fileutils'
4
4
 
5
+ # for valgrind
6
+ at_exit { GC.start }
7
+
5
8
  SPEC_ROOT = File.dirname(__FILE__)
6
9
  TEMP_ROOT = File.join(SPEC_ROOT, 'tmp')
7
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lmdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: '0.6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-22 00:00:00.000000000 Z
11
+ date: 2022-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby_memcheck
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
55
69
  description: lmdb is a Ruby binding to OpenLDAP Lightning MDB.
56
70
  email: mail@daniel-mendler.de
57
71
  executables: []
@@ -66,6 +80,7 @@ files:
66
80
  - Gemfile
67
81
  - README.md
68
82
  - Rakefile
83
+ - behaviour.org
69
84
  - ext/lmdb_ext/cursor_delete_flags.h
70
85
  - ext/lmdb_ext/cursor_put_flags.h
71
86
  - ext/lmdb_ext/dbi_flags.h
@@ -95,7 +110,7 @@ homepage: https://github.com/minad/lmdb
95
110
  licenses:
96
111
  - MIT
97
112
  metadata: {}
98
- post_install_message:
113
+ post_install_message:
99
114
  rdoc_options: []
100
115
  require_paths:
101
116
  - lib
@@ -110,8 +125,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
125
  - !ruby/object:Gem::Version
111
126
  version: '0'
112
127
  requirements: []
113
- rubygems_version: 3.1.2
114
- signing_key:
128
+ rubygems_version: 3.3.11
129
+ signing_key:
115
130
  specification_version: 4
116
131
  summary: Ruby bindings to Lightning MDB
117
132
  test_files: