lmdb 0.6 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/lmdb_ext/extconf.rb +7 -3
- data/ext/lmdb_ext/lmdb_ext.c +126 -113
- data/lib/lmdb/version.rb +1 -1
- data/lmdb.gemspec +11 -9
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/.gitignore +8 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/COPYRIGHT +1 -1
- data/vendor/libraries/liblmdb/Doxyfile +1631 -0
- data/vendor/libraries/liblmdb/Makefile +118 -0
- data/vendor/libraries/liblmdb/intro.doc +192 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/lmdb.h +161 -61
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/mdb.c +3244 -1302
- data/vendor/libraries/liblmdb/mdb_copy.1 +61 -0
- data/vendor/libraries/liblmdb/mdb_copy.c +84 -0
- data/vendor/libraries/liblmdb/mdb_drop.1 +40 -0
- data/vendor/libraries/liblmdb/mdb_drop.c +135 -0
- data/vendor/libraries/liblmdb/mdb_dump.1 +81 -0
- data/vendor/libraries/liblmdb/mdb_dump.c +319 -0
- data/vendor/libraries/liblmdb/mdb_load.1 +84 -0
- data/vendor/libraries/liblmdb/mdb_load.c +492 -0
- data/vendor/libraries/liblmdb/mdb_stat.1 +70 -0
- data/vendor/libraries/liblmdb/mdb_stat.c +264 -0
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.c +66 -5
- data/{ext/lmdb_ext → vendor/libraries}/liblmdb/midl.h +19 -5
- data/vendor/libraries/liblmdb/mtest.c +177 -0
- data/vendor/libraries/liblmdb/mtest2.c +124 -0
- data/vendor/libraries/liblmdb/mtest3.c +133 -0
- data/vendor/libraries/liblmdb/mtest4.c +168 -0
- data/vendor/libraries/liblmdb/mtest5.c +135 -0
- data/vendor/libraries/liblmdb/mtest6.c +141 -0
- data/vendor/libraries/liblmdb/sample-bdb.txt +73 -0
- data/vendor/libraries/liblmdb/sample-mdb.txt +62 -0
- data/vendor/libraries/liblmdb/tooltag +27 -0
- metadata +44 -24
- data/.gitignore +0 -15
- data/.travis.yml +0 -14
- data/ext/lmdb_ext/liblmdb/CHANGES +0 -112
- /data/{ext/lmdb_ext → vendor/libraries}/liblmdb/LICENSE +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce93aeb587bfdacbda4c767a85fc71d7207707cb3ac8f11a614bd6b9478a6c5d
|
4
|
+
data.tar.gz: 9ae5404da5dccaeac5be828f3ea3710b81c2f4942642370d706b89b7d2183806
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '038bc6f20a16aa7ef463e5e6bdd25c4db7529aa0c261582890a0e3171c5c2203d124ba1bb281a88852eaf09ad125636ca1affff3560bb4dd7daeab8eeb894d45'
|
7
|
+
data.tar.gz: c4eb231408c47a79054d52055ac3c4b1a3b5f8d02937aabddc12fb854ed3f5eb4874db9aa73434aeb320365c74538fa896a39a534fa0014e85691f1a87b17fab
|
data/ext/lmdb_ext/extconf.rb
CHANGED
@@ -5,10 +5,14 @@ $CFLAGS << ' -fdeclspec' if /darwin/.match? RUBY_PLATFORM
|
|
5
5
|
|
6
6
|
# Embed lmdb if we cannot find it
|
7
7
|
if enable_config("bundled-lmdb", false) || !(find_header('lmdb.h') && have_library('lmdb', 'mdb_env_create'))
|
8
|
-
|
8
|
+
lmdbpath = "../../vendor/libraries/liblmdb"
|
9
|
+
$INCFLAGS << " -I$(srcdir)/#{lmdbpath}"
|
9
10
|
$VPATH ||= []
|
10
|
-
$VPATH << "$(srcdir)
|
11
|
-
|
11
|
+
$VPATH << "$(srcdir)/#{lmdbpath}"
|
12
|
+
# XXX this is a sketchy, sketchy way to do this
|
13
|
+
$srcs = Dir.glob("#{$srcdir}/{#{lmdbpath}/{mdb,midl}.c,*.c}").map do |n|
|
14
|
+
File.basename(n)
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
have_header 'limits.h'
|
data/ext/lmdb_ext/lmdb_ext.c
CHANGED
@@ -38,21 +38,21 @@ static void check(int code) {
|
|
38
38
|
}
|
39
39
|
|
40
40
|
static void transaction_free(Transaction* transaction) {
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
if (transaction->txn) {
|
42
|
+
//int id = (int)mdb_txn_id(transaction->txn);
|
43
|
+
//rb_warn(sprintf("Memory leak: Garbage collecting active transaction %d", id));
|
44
|
+
rb_warn("Memory leak: Garbage collecting active transaction");
|
45
|
+
// transaction_abort(transaction);
|
46
|
+
// mdb_txn_abort(transaction->txn);
|
47
|
+
}
|
48
|
+
free(transaction);
|
49
49
|
}
|
50
50
|
|
51
51
|
static void transaction_mark(Transaction* transaction) {
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
rb_gc_mark(transaction->parent);
|
53
|
+
rb_gc_mark(transaction->child);
|
54
|
+
rb_gc_mark(transaction->env);
|
55
|
+
rb_gc_mark(transaction->cursors);
|
56
56
|
}
|
57
57
|
|
58
58
|
/**
|
@@ -197,7 +197,8 @@ static void transaction_finish(VALUE self, int commit) {
|
|
197
197
|
// no more active read-write transaction; unset the registry
|
198
198
|
if (!(transaction->flags & MDB_RDONLY) && !transaction->parent) {
|
199
199
|
ENVIRONMENT(transaction->env, env);
|
200
|
-
|
200
|
+
// maybe this should be Qnil, i dunno
|
201
|
+
env->rw_txn_thread = (VALUE)NULL;
|
201
202
|
}
|
202
203
|
|
203
204
|
// now set the active transaction to the parent, if there is one
|
@@ -296,8 +297,6 @@ static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flag
|
|
296
297
|
if (vparent && !NIL_P(vparent))
|
297
298
|
Data_Get_Struct(vparent, Transaction, tparent);
|
298
299
|
|
299
|
-
// rb_warn("fart lol");
|
300
|
-
|
301
300
|
// XXX note this is a cursed goto loop that could almost certainly
|
302
301
|
// be rewritten as a do-while
|
303
302
|
retry:
|
@@ -600,47 +599,50 @@ static int environment_options(VALUE key, VALUE value, EnvironmentOptions* optio
|
|
600
599
|
* end
|
601
600
|
*/
|
602
601
|
static VALUE environment_new(int argc, VALUE *argv, VALUE klass) {
|
603
|
-
|
602
|
+
VALUE path, option_hash;
|
604
603
|
|
605
604
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
606
|
-
|
607
|
-
|
605
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
606
|
+
argc, argv, "1:", &path, &option_hash);
|
608
607
|
#else
|
609
|
-
|
608
|
+
rb_scan_args(argc, argv, "1:", &path, &option_hash);
|
610
609
|
#endif
|
611
610
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
check(mdb_env_set_maxreaders(env, options.maxreaders));
|
633
|
-
if (options.mapsize > 0)
|
634
|
-
check(mdb_env_set_mapsize(env, options.mapsize));
|
635
|
-
|
636
|
-
check(mdb_env_set_maxdbs(env, options.maxdbs <= 0 ? 1 : options.maxdbs));
|
637
|
-
VALUE expanded_path = rb_file_expand_path(path, Qnil);
|
638
|
-
check(mdb_env_open(env, StringValueCStr(expanded_path), options.flags, options.mode));
|
611
|
+
EnvironmentOptions options = {
|
612
|
+
.flags = MDB_NOTLS,
|
613
|
+
.maxreaders = -1,
|
614
|
+
.maxdbs = 128,
|
615
|
+
.mapsize = 0,
|
616
|
+
.mode = 0755,
|
617
|
+
};
|
618
|
+
if (!NIL_P(option_hash))
|
619
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))environment_options,
|
620
|
+
(VALUE)&options);
|
621
|
+
|
622
|
+
MDB_env* env;
|
623
|
+
check(mdb_env_create(&env));
|
624
|
+
|
625
|
+
Environment* environment;
|
626
|
+
VALUE venv = Data_Make_Struct(cEnvironment, Environment, environment_mark,
|
627
|
+
environment_free, environment);
|
628
|
+
environment->env = env;
|
629
|
+
environment->thread_txn_hash = rb_hash_new();
|
630
|
+
environment->txn_thread_hash = rb_hash_new();
|
639
631
|
|
640
|
-
|
641
|
-
|
632
|
+
if (options.maxreaders > 0)
|
633
|
+
check(mdb_env_set_maxreaders(env, options.maxreaders));
|
634
|
+
if (options.mapsize > 0)
|
635
|
+
check(mdb_env_set_mapsize(env, options.mapsize));
|
642
636
|
|
643
|
-
|
637
|
+
check(mdb_env_set_maxdbs(env, options.maxdbs <= 0 ? 1 : options.maxdbs));
|
638
|
+
VALUE expanded_path = rb_file_expand_path(path, Qnil);
|
639
|
+
check(mdb_env_open(env, StringValueCStr(expanded_path), options.flags,
|
640
|
+
options.mode));
|
641
|
+
|
642
|
+
if (rb_block_given_p())
|
643
|
+
return rb_ensure(rb_yield, venv, environment_close, venv);
|
644
|
+
|
645
|
+
return venv;
|
644
646
|
}
|
645
647
|
|
646
648
|
/**
|
@@ -877,32 +879,35 @@ static void database_mark(Database* database) {
|
|
877
879
|
* transaction or a read-only environment.
|
878
880
|
*/
|
879
881
|
static VALUE environment_database(int argc, VALUE *argv, VALUE self) {
|
880
|
-
|
881
|
-
|
882
|
-
|
882
|
+
ENVIRONMENT(self, environment);
|
883
|
+
if (!active_txn(self))
|
884
|
+
return call_with_transaction(self, self, "database", argc, argv, 0);
|
883
885
|
|
884
|
-
|
886
|
+
VALUE name, option_hash;
|
885
887
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
886
|
-
|
887
|
-
|
888
|
+
rb_scan_args_kw(RB_SCAN_ARGS_KEYWORDS,
|
889
|
+
argc, argv, "01:", &name, &option_hash);
|
888
890
|
#else
|
889
|
-
|
891
|
+
rb_scan_args(argc, argv, "01:", &name, &option_hash);
|
890
892
|
#endif
|
891
893
|
|
892
894
|
|
893
|
-
|
894
|
-
|
895
|
-
|
895
|
+
int flags = 0;
|
896
|
+
if (!NIL_P(option_hash))
|
897
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))database_flags,
|
898
|
+
(VALUE)&flags);
|
896
899
|
|
897
|
-
|
898
|
-
|
900
|
+
MDB_dbi dbi;
|
901
|
+
check(mdb_dbi_open(need_txn(self), NIL_P(name) ? 0 : StringValueCStr(name),
|
902
|
+
flags, &dbi));
|
899
903
|
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
+
Database* database;
|
905
|
+
VALUE vdb = Data_Make_Struct(cDatabase, Database, database_mark, free,
|
906
|
+
database);
|
907
|
+
database->dbi = dbi;
|
908
|
+
database->env = self;
|
904
909
|
|
905
|
-
|
910
|
+
return vdb;
|
906
911
|
}
|
907
912
|
|
908
913
|
/**
|
@@ -1064,33 +1069,34 @@ static VALUE database_get(VALUE self, VALUE vkey) {
|
|
1064
1069
|
* data.
|
1065
1070
|
*/
|
1066
1071
|
static VALUE database_put(int argc, VALUE *argv, VALUE self) {
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1072
|
+
DATABASE(self, database);
|
1073
|
+
if (!active_txn(database->env))
|
1074
|
+
return call_with_transaction(database->env, self, "put", argc, argv, 0);
|
1070
1075
|
|
1071
|
-
|
1076
|
+
VALUE vkey, vval, option_hash = Qnil;
|
1072
1077
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
1073
|
-
|
1074
|
-
|
1078
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1079
|
+
argc, argv, "20:", &vkey, &vval, &option_hash);
|
1075
1080
|
#else
|
1076
|
-
|
1081
|
+
rb_scan_args(argc, argv, "20:", &vkey, &vval, &option_hash);
|
1077
1082
|
#endif
|
1078
1083
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1084
|
+
int flags = 0;
|
1085
|
+
if (!NIL_P(option_hash))
|
1086
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))database_put_flags,
|
1087
|
+
(VALUE)&flags);
|
1082
1088
|
|
1083
|
-
|
1084
|
-
|
1089
|
+
vkey = StringValue(vkey);
|
1090
|
+
vval = StringValue(vval);
|
1085
1091
|
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1092
|
+
MDB_val key, value;
|
1093
|
+
key.mv_size = RSTRING_LEN(vkey);
|
1094
|
+
key.mv_data = RSTRING_PTR(vkey);
|
1095
|
+
value.mv_size = RSTRING_LEN(vval);
|
1096
|
+
value.mv_data = RSTRING_PTR(vval);
|
1091
1097
|
|
1092
|
-
|
1093
|
-
|
1098
|
+
check(mdb_put(need_txn(database->env), database->dbi, &key, &value, flags));
|
1099
|
+
return Qnil;
|
1094
1100
|
}
|
1095
1101
|
|
1096
1102
|
/**
|
@@ -1454,31 +1460,32 @@ static VALUE cursor_get(VALUE self) {
|
|
1454
1460
|
* data.
|
1455
1461
|
*/
|
1456
1462
|
static VALUE cursor_put(int argc, VALUE* argv, VALUE self) {
|
1457
|
-
|
1463
|
+
CURSOR(self, cursor);
|
1458
1464
|
|
1459
|
-
|
1465
|
+
VALUE vkey, vval, option_hash;
|
1460
1466
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
1461
|
-
|
1462
|
-
|
1467
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1468
|
+
argc, argv, "2:", &vkey, &vval, &option_hash);
|
1463
1469
|
#else
|
1464
|
-
|
1470
|
+
rb_scan_args(argc, argv, "2:", &vkey, &vval, &option_hash);
|
1465
1471
|
#endif
|
1466
1472
|
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1473
|
+
int flags = 0;
|
1474
|
+
if (!NIL_P(option_hash))
|
1475
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))cursor_put_flags,
|
1476
|
+
(VALUE)&flags);
|
1470
1477
|
|
1471
|
-
|
1472
|
-
|
1478
|
+
vkey = StringValue(vkey);
|
1479
|
+
vval = StringValue(vval);
|
1473
1480
|
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1481
|
+
MDB_val key, value;
|
1482
|
+
key.mv_size = RSTRING_LEN(vkey);
|
1483
|
+
key.mv_data = RSTRING_PTR(vkey);
|
1484
|
+
value.mv_size = RSTRING_LEN(vval);
|
1485
|
+
value.mv_data = RSTRING_PTR(vval);
|
1479
1486
|
|
1480
|
-
|
1481
|
-
|
1487
|
+
check(mdb_cursor_put(cursor->cur, &key, &value, flags));
|
1488
|
+
return Qnil;
|
1482
1489
|
}
|
1483
1490
|
|
1484
1491
|
#define METHOD cursor_delete_flags
|
@@ -1496,22 +1503,23 @@ static VALUE cursor_put(int argc, VALUE* argv, VALUE self) {
|
|
1496
1503
|
* if the database was opened with +:dupsort+.
|
1497
1504
|
*/
|
1498
1505
|
static VALUE cursor_delete(int argc, VALUE *argv, VALUE self) {
|
1499
|
-
|
1506
|
+
CURSOR(self, cursor);
|
1500
1507
|
|
1501
|
-
|
1508
|
+
VALUE option_hash;
|
1502
1509
|
#ifdef RB_SCAN_ARGS_KEYWORDS
|
1503
|
-
|
1504
|
-
|
1510
|
+
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS,
|
1511
|
+
argc, argv, ":", &option_hash);
|
1505
1512
|
#else
|
1506
|
-
|
1513
|
+
rb_scan_args(argc, argv, ":", &option_hash);
|
1507
1514
|
#endif
|
1508
1515
|
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1516
|
+
int flags = 0;
|
1517
|
+
if (!NIL_P(option_hash))
|
1518
|
+
rb_hash_foreach(option_hash, (int (*)(ANYARGS))cursor_delete_flags,
|
1519
|
+
(VALUE)&flags);
|
1512
1520
|
|
1513
|
-
|
1514
|
-
|
1521
|
+
check(mdb_cursor_del(cursor->cur, flags));
|
1522
|
+
return Qnil;
|
1515
1523
|
}
|
1516
1524
|
|
1517
1525
|
/**
|
@@ -1562,7 +1570,8 @@ void Init_lmdb_ext() {
|
|
1562
1570
|
* A general class of exceptions raised within the LMDB gem.
|
1563
1571
|
*/
|
1564
1572
|
cError = rb_define_class_under(mLMDB, "Error", rb_eRuntimeError);
|
1565
|
-
#define ERROR(name)
|
1573
|
+
#define ERROR(name) \
|
1574
|
+
cError_##name = rb_define_class_under(cError, #name, cError);
|
1566
1575
|
#include "errors.h"
|
1567
1576
|
#undef ERROR
|
1568
1577
|
|
@@ -1591,6 +1600,7 @@ void Init_lmdb_ext() {
|
|
1591
1600
|
* env.close
|
1592
1601
|
*/
|
1593
1602
|
cEnvironment = rb_define_class_under(mLMDB, "Environment", rb_cObject);
|
1603
|
+
rb_undef_alloc_func(cEnvironment);
|
1594
1604
|
rb_define_singleton_method(cEnvironment, "new", environment_new, -1);
|
1595
1605
|
rb_define_method(cEnvironment, "database", environment_database, -1);
|
1596
1606
|
rb_define_method(cEnvironment, "active_txn", environment_active_txn, 0);
|
@@ -1632,6 +1642,7 @@ void Init_lmdb_ext() {
|
|
1632
1642
|
* env.close
|
1633
1643
|
*/
|
1634
1644
|
cDatabase = rb_define_class_under(mLMDB, "Database", rb_cObject);
|
1645
|
+
rb_undef_alloc_func(cDatabase);
|
1635
1646
|
rb_undef_method(rb_singleton_class(cDatabase), "new");
|
1636
1647
|
rb_define_method(cDatabase, "stat", database_stat, 0);
|
1637
1648
|
rb_define_method(cDatabase, "flags", database_get_flags, 0);
|
@@ -1703,6 +1714,7 @@ void Init_lmdb_ext() {
|
|
1703
1714
|
* #=> storage.
|
1704
1715
|
*/
|
1705
1716
|
cTransaction = rb_define_class_under(mLMDB, "Transaction", rb_cObject);
|
1717
|
+
rb_undef_alloc_func(cTransaction);
|
1706
1718
|
rb_undef_method(rb_singleton_class(cTransaction), "new");
|
1707
1719
|
rb_define_method(cTransaction, "commit", transaction_commit, 0);
|
1708
1720
|
rb_define_method(cTransaction, "abort", transaction_abort, 0);
|
@@ -1736,6 +1748,7 @@ void Init_lmdb_ext() {
|
|
1736
1748
|
* end
|
1737
1749
|
*/
|
1738
1750
|
cCursor = rb_define_class_under(mLMDB, "Cursor", rb_cObject);
|
1751
|
+
rb_undef_alloc_func(cCursor);
|
1739
1752
|
rb_undef_method(rb_singleton_class(cCursor), "new");
|
1740
1753
|
rb_define_method(cCursor, "close", cursor_close, 0);
|
1741
1754
|
rb_define_method(cCursor, "get", cursor_get, 0);
|
data/lib/lmdb/version.rb
CHANGED
data/lmdb.gemspec
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
# -*- encoding: utf-8 -*-
|
2
3
|
require File.dirname(__FILE__) + '/lib/lmdb/version'
|
3
4
|
require 'date'
|
@@ -9,19 +10,20 @@ Gem::Specification.new do |s|
|
|
9
10
|
s.date = Date.today.to_s
|
10
11
|
s.licenses = ['MIT']
|
11
12
|
s.summary = 'Ruby bindings to Lightning MDB'
|
12
|
-
s.email = '
|
13
|
-
s.homepage = 'https://github.com/
|
13
|
+
s.email = 'code@doriantaylor.com'
|
14
|
+
s.homepage = 'https://github.com/doriantaylor/rb-lmdb'
|
14
15
|
s.description = 'lmdb is a Ruby binding to OpenLDAP Lightning MDB.'
|
15
|
-
s.authors = ['Daniel Mendler']
|
16
|
+
s.authors = ['Daniel Mendler', 'Dorian Taylor']
|
16
17
|
s.extensions = Dir['ext/**/extconf.rb']
|
17
18
|
|
18
|
-
s.files = `git ls-files
|
19
|
+
s.files = `git ls-files --recurse-submodules -- *`.split("\n")
|
19
20
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
20
21
|
s.require_paths = ['lib']
|
21
22
|
|
22
|
-
s.required_ruby_version =
|
23
|
-
|
24
|
-
s.add_development_dependency 'rake
|
25
|
-
s.add_development_dependency '
|
26
|
-
s.add_development_dependency '
|
23
|
+
s.required_ruby_version = '>= 2.7'
|
24
|
+
|
25
|
+
s.add_development_dependency 'rake', '~> 13'
|
26
|
+
s.add_development_dependency 'rake-compiler', '~> 1.2'
|
27
|
+
s.add_development_dependency 'rspec', '~> 3'
|
28
|
+
s.add_development_dependency 'ruby_memcheck', '~> 2'
|
27
29
|
end
|