fat 0.0.5 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +5 -3
- data/benchmarks/fat.rb +87 -62
- data/ext/fat/fat.c +60 -38
- data/fat.gemspec +1 -1
- data/test/fat_test.rb +15 -13
- data/test/memleak.rb +11 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cb164e13d70c6ba83c7a6388dee09b09f1c16ad
|
4
|
+
data.tar.gz: c83ade83a11961ed0e878f4be1f983864d02f185
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcbb1ecdb3c05e52ce5575fc5c4f5f9c46c25ab591e63237fcbe5b7efb1ec78385b7e8e4037c8bf614095486de2bd48de102d1817c24e4699859e47fdf5831b8
|
7
|
+
data.tar.gz: 1c4e40eafe1e97a2eee88b5f2042d3189575d3fadd223ec0c30570c49546395a4d9b1f238bdd2f30e4af202bbdde40a05fc0af05f836cad0266f439eebe48b80
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -21,7 +21,7 @@ hash = {
|
|
21
21
|
|
22
22
|
To get your `:value` you usually do `hash["foo"]["bar"]["baz"]`. But what happens if `"bar"` doesn't exist? Yeap, BOOM!
|
23
23
|
|
24
|
-
I find more comfortable to ask if I can walk to `:value` using the keys `"foo"`, `"bar"`, `"baz"`. If I can't,
|
24
|
+
I find more comfortable to ask if I can walk to `:value` using the keys `"foo"`, `"bar"`, `"baz"`. If I can't, tell me where the path ends.
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
require "fat"
|
@@ -30,9 +30,11 @@ Fat.at(hash, "foo", "bar", "baz")
|
|
30
30
|
# => :value
|
31
31
|
|
32
32
|
Fat.at(hash, "foo", "not", "here")
|
33
|
-
# =>
|
33
|
+
# => Fat::FatError: No hash found at foo.not
|
34
34
|
```
|
35
35
|
|
36
|
+
The `Fat::FatError` let's you know that a `Hash` was expected as a value for the key `"not"`. Way more explicit than guessing from a `undefined method [] for nil`.
|
37
|
+
|
36
38
|
It's the same with Symbols
|
37
39
|
|
38
40
|
```ruby
|
@@ -57,7 +59,7 @@ hash.at("foo", "bar", "baz")
|
|
57
59
|
# => :value
|
58
60
|
```
|
59
61
|
|
60
|
-
|
62
|
+
If all your keys are Strings you can *namespace* them with dots.
|
61
63
|
|
62
64
|
```ruby
|
63
65
|
Hash.include(Fat)
|
data/benchmarks/fat.rb
CHANGED
@@ -8,10 +8,9 @@ class Hash
|
|
8
8
|
fields = args.length == 1 ? args[0].split(".") : args
|
9
9
|
value = self
|
10
10
|
|
11
|
-
fields.
|
12
|
-
value =
|
13
|
-
|
14
|
-
return value unless value.kind_of?(Hash)
|
11
|
+
fields[0..-2].each_with_index do |field, index|
|
12
|
+
value = value[field]
|
13
|
+
raise Fat::FatError, "No hash found at #{fields[0..index].join(".")}" unless value.kind_of?(Hash)
|
15
14
|
end
|
16
15
|
|
17
16
|
value
|
@@ -30,21 +29,23 @@ hash = {
|
|
30
29
|
|
31
30
|
puts "### String chain as a single argument."
|
32
31
|
Benchmark.ips do |bench|
|
33
|
-
bench.report("ruby") { hash.ruby_at("foo.bar.baz") }
|
34
|
-
bench.report("c") { hash.at("foo.bar.baz") }
|
32
|
+
bench.report("ruby") { hash.ruby_at("foo.bar.baz.key") }
|
33
|
+
bench.report("c") { hash.at("foo.bar.baz.key") }
|
35
34
|
bench.compare!
|
36
35
|
end
|
37
36
|
|
38
|
-
|
37
|
+
### String chain as a single argument.
|
39
38
|
# Calculating -------------------------------------
|
40
|
-
# ruby
|
41
|
-
# c
|
39
|
+
# ruby 37856 i/100ms
|
40
|
+
# c 47169 i/100ms
|
42
41
|
# -------------------------------------------------
|
43
|
-
# ruby
|
44
|
-
# c
|
42
|
+
# ruby 538803.8 (±7.3%) i/s - 2687776 in 5.026742s
|
43
|
+
# c 722313.8 (±7.2%) i/s - 3584844 in 5.006989s
|
44
|
+
#
|
45
45
|
# Comparison:
|
46
|
-
# c:
|
47
|
-
# ruby:
|
46
|
+
# c: 722313.8 i/s
|
47
|
+
# ruby: 538803.8 i/s - 1.34x slower
|
48
|
+
|
48
49
|
|
49
50
|
puts "### Each key as an argument."
|
50
51
|
Benchmark.ips do |bench|
|
@@ -53,84 +54,108 @@ Benchmark.ips do |bench|
|
|
53
54
|
bench.compare!
|
54
55
|
end
|
55
56
|
|
56
|
-
|
57
|
+
### Each key as an argument.
|
57
58
|
# Calculating -------------------------------------
|
58
|
-
# ruby
|
59
|
-
# c
|
59
|
+
# ruby 59783 i/100ms
|
60
|
+
# c 74832 i/100ms
|
60
61
|
# -------------------------------------------------
|
61
|
-
# ruby
|
62
|
-
# c
|
62
|
+
# ruby 1074906.7 (±3.1%) i/s - 5380470 in 5.010624s
|
63
|
+
# c 1590070.0 (±4.9%) i/s - 7932192 in 5.004163s
|
64
|
+
#
|
63
65
|
# Comparison:
|
64
|
-
# c:
|
65
|
-
# ruby:
|
66
|
+
# c: 1590070.0 i/s
|
67
|
+
# ruby: 1074906.7 i/s - 1.48x slower
|
66
68
|
|
67
69
|
puts "### No value found."
|
68
70
|
Benchmark.ips do |bench|
|
69
|
-
bench.report("ruby") { hash.ruby_at("foo.one.key") }
|
70
|
-
bench.report("c") { hash.at("foo.one.key") }
|
71
|
+
bench.report("ruby") { hash.ruby_at("foo.one.key") rescue Fat::FatError }
|
72
|
+
bench.report("c") { hash.at("foo.one.key") rescue Fat::FatError }
|
71
73
|
bench.compare!
|
72
74
|
end
|
73
75
|
|
74
|
-
|
76
|
+
### No value found.
|
75
77
|
# Calculating -------------------------------------
|
76
|
-
# ruby
|
77
|
-
# c
|
78
|
+
# ruby 17493 i/100ms
|
79
|
+
# c 18993 i/100ms
|
78
80
|
# -------------------------------------------------
|
79
|
-
# ruby
|
80
|
-
# c
|
81
|
+
# ruby 208018.1 (±4.9%) i/s - 1049580 in 5.060661s
|
82
|
+
# c 225204.0 (±5.1%) i/s - 1139580 in 5.074972s
|
83
|
+
#
|
81
84
|
# Comparison:
|
82
|
-
# c:
|
83
|
-
# ruby:
|
85
|
+
# c: 225204.0 i/s
|
86
|
+
# ruby: 208018.1 i/s - 1.08x slower
|
87
|
+
|
88
|
+
deep_hash = {}
|
89
|
+
1.upto(100) do |n|
|
90
|
+
key = (1...n).to_a.join(".")
|
91
|
+
current_hash = deep_hash.at(key)
|
92
|
+
current_hash[n.to_s] = {}
|
93
|
+
end
|
84
94
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
"one" => {
|
91
|
-
"more" => {
|
92
|
-
"key" => :value
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
}
|
97
|
-
}
|
98
|
-
}
|
99
|
-
}
|
95
|
+
path_to_100 = 1.upto(100).to_a.map(&:to_s)
|
96
|
+
|
97
|
+
deep_hash.at(path_to_100.join("."))["foo"] = :bar
|
98
|
+
|
99
|
+
path_to_foo = path_to_100 << "foo"
|
100
100
|
|
101
101
|
puts "### Deep hash - String chain argument."
|
102
102
|
Benchmark.ips do |bench|
|
103
|
-
bench.report("ruby") { deep_hash.ruby_at("
|
104
|
-
bench.report("c") { deep_hash.at("
|
103
|
+
bench.report("ruby") { deep_hash.ruby_at(path_to_foo.join(".")) }
|
104
|
+
bench.report("c") { deep_hash.at(path_to_foo.join(".")) }
|
105
105
|
bench.compare!
|
106
106
|
end
|
107
107
|
|
108
|
-
|
108
|
+
### Deep hash - String chain argument.
|
109
109
|
# Calculating -------------------------------------
|
110
|
-
# ruby
|
111
|
-
# c
|
110
|
+
# ruby 2549 i/100ms
|
111
|
+
# c 3633 i/100ms
|
112
112
|
# -------------------------------------------------
|
113
|
-
# ruby
|
114
|
-
# c
|
113
|
+
# ruby 27061.4 (±4.1%) i/s - 135097 in 5.002624s
|
114
|
+
# c 37773.5 (±3.0%) i/s - 188916 in 5.006074s
|
115
|
+
#
|
115
116
|
# Comparison:
|
116
|
-
# c:
|
117
|
-
# ruby:
|
117
|
+
# c: 37773.5 i/s
|
118
|
+
# ruby: 27061.4 i/s - 1.40x slower
|
118
119
|
|
119
120
|
puts "### Deep hash - Each key as an argument."
|
120
121
|
Benchmark.ips do |bench|
|
121
|
-
bench.report("ruby") { deep_hash.ruby_at(
|
122
|
-
bench.report("c") { deep_hash.at(
|
122
|
+
bench.report("ruby") { deep_hash.ruby_at(*path_to_foo) }
|
123
|
+
bench.report("c") { deep_hash.at(*path_to_foo) }
|
124
|
+
bench.compare!
|
125
|
+
end
|
126
|
+
|
127
|
+
### Deep hash - Each key as an argument.
|
128
|
+
# Calculating -------------------------------------
|
129
|
+
# ruby 4812 i/100ms
|
130
|
+
# c 14712 i/100ms
|
131
|
+
# -------------------------------------------------
|
132
|
+
# ruby 49779.4 (±5.1%) i/s - 250224 in 5.042241s
|
133
|
+
# c 168563.4 (±3.4%) i/s - 853296 in 5.068311s
|
134
|
+
#
|
135
|
+
# Comparison:
|
136
|
+
# c: 168563.4 i/s
|
137
|
+
# ruby: 49779.4 i/s - 3.39x slower
|
138
|
+
|
139
|
+
path_to_not = 1.upto(99).to_a.map(&:to_s)
|
140
|
+
path_to_not << "not"
|
141
|
+
path_to_not << "100"
|
142
|
+
|
143
|
+
puts "### Deep hash - No value found."
|
144
|
+
Benchmark.ips do |bench|
|
145
|
+
bench.report("ruby") { deep_hash.ruby_at(*path_to_not) rescue Fat::FatError }
|
146
|
+
bench.report("c") { deep_hash.at(*path_to_not) rescue Fat::FatError }
|
123
147
|
bench.compare!
|
124
148
|
end
|
125
149
|
|
126
|
-
|
150
|
+
### Deep hash - No value found.
|
127
151
|
# Calculating -------------------------------------
|
128
|
-
# ruby
|
129
|
-
# c
|
152
|
+
# ruby 2787 i/100ms
|
153
|
+
# c 8277 i/100ms
|
130
154
|
# -------------------------------------------------
|
131
|
-
# ruby
|
132
|
-
# c
|
155
|
+
# ruby 30667.8 (±1.3%) i/s - 156072 in 5.090009s
|
156
|
+
# c 86884.1 (±5.1%) i/s - 438681 in 5.064367s
|
157
|
+
#
|
133
158
|
# Comparison:
|
134
|
-
# c:
|
135
|
-
# ruby:
|
159
|
+
# c: 86884.1 i/s
|
160
|
+
# ruby: 30667.8 i/s - 2.83x slower
|
136
161
|
|
data/ext/fat/fat.c
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
#include<ruby.h>
|
2
2
|
|
3
3
|
VALUE Fat = Qnil;
|
4
|
+
VALUE rb_eFatError = Qnil;
|
4
5
|
|
5
6
|
void Init_fat();
|
6
7
|
|
7
8
|
// Interface methods
|
8
9
|
static VALUE singleton_method_at(int argc, VALUE *argv, VALUE self);
|
9
|
-
static VALUE singleton_method_fetch_at(int argc, VALUE *argv, VALUE self);
|
10
10
|
static VALUE method_at(int argc, VALUE *argv, VALUE hash);
|
11
|
-
static VALUE method_fetch_at(int argc, VALUE *argv, VALUE hash);
|
12
11
|
|
13
|
-
static VALUE fat(VALUE hash, VALUE fields
|
12
|
+
static VALUE fat(VALUE hash, VALUE fields);
|
14
13
|
|
15
14
|
// Helpers
|
16
15
|
static inline void parse_fields(VALUE args, VALUE *fields);
|
17
16
|
static inline VALUE fields_upto_index(VALUE fields, long index);
|
18
17
|
static inline void parse_singleton_args(int argc, VALUE *argv, VALUE *hash, VALUE *fields);
|
19
18
|
static inline void parse_method_args(int argc, VALUE *argv, VALUE *fields);
|
19
|
+
static inline long compute_error_message_length(VALUE fields, long index);
|
20
|
+
static inline void copy_error_message(VALUE fields, long index, char* error_message_pointer);
|
20
21
|
|
21
22
|
void Init_fat(void) {
|
22
23
|
Fat = rb_define_module("Fat");
|
24
|
+
rb_eFatError = rb_define_class_under(Fat, "FatError", rb_eStandardError);
|
23
25
|
|
24
26
|
rb_define_module_function(Fat, "at", singleton_method_at, -1);
|
25
|
-
rb_define_module_function(Fat, "fetch_at", singleton_method_fetch_at, -1);
|
26
27
|
rb_define_method(Fat, "at", method_at, -1);
|
27
|
-
rb_define_method(Fat, "fetch_at", method_fetch_at, -1);
|
28
28
|
}
|
29
29
|
|
30
30
|
static VALUE singleton_method_at(int argc, VALUE *argv, VALUE self) {
|
@@ -33,16 +33,7 @@ static VALUE singleton_method_at(int argc, VALUE *argv, VALUE self) {
|
|
33
33
|
|
34
34
|
parse_singleton_args(argc, argv, &hash, &fields);
|
35
35
|
|
36
|
-
return fat(hash, fields
|
37
|
-
}
|
38
|
-
|
39
|
-
static VALUE singleton_method_fetch_at(int argc, VALUE *argv, VALUE self) {
|
40
|
-
VALUE hash;
|
41
|
-
VALUE fields;
|
42
|
-
|
43
|
-
parse_singleton_args(argc, argv, &hash, &fields);
|
44
|
-
|
45
|
-
return fat(hash, fields, 1);
|
36
|
+
return fat(hash, fields);
|
46
37
|
}
|
47
38
|
|
48
39
|
static VALUE method_at(int argc, VALUE *argv, VALUE hash) {
|
@@ -50,33 +41,17 @@ static VALUE method_at(int argc, VALUE *argv, VALUE hash) {
|
|
50
41
|
|
51
42
|
parse_method_args(argc, argv, &fields);
|
52
43
|
|
53
|
-
return fat(hash, fields
|
54
|
-
}
|
55
|
-
|
56
|
-
static VALUE method_fetch_at(int argc, VALUE *argv, VALUE hash) {
|
57
|
-
VALUE fields;
|
58
|
-
|
59
|
-
parse_method_args(argc, argv, &fields);
|
60
|
-
|
61
|
-
return fat(hash, fields, 1);
|
44
|
+
return fat(hash, fields);
|
62
45
|
}
|
63
46
|
|
64
|
-
static VALUE fat(VALUE hash, VALUE fields
|
47
|
+
static VALUE fat(VALUE hash, VALUE fields) {
|
65
48
|
VALUE value = hash;
|
66
49
|
|
67
50
|
for (long i = 0; i < RARRAY_LEN(fields); i++) {
|
68
51
|
value = rb_hash_aref(value, RARRAY_AREF(fields, i));
|
69
52
|
|
70
|
-
if (value
|
71
|
-
|
72
|
-
rb_raise(rb_eKeyError, "No value found at %s", RSTRING_PTR(fields_upto_index(fields, i)));
|
73
|
-
} else {
|
74
|
-
return Qnil;
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
if (TYPE(value) != T_HASH) {
|
79
|
-
return value;
|
53
|
+
if (i < RARRAY_LEN(fields) - 1 && TYPE(value) != T_HASH) {
|
54
|
+
rb_raise(rb_eFatError, "No hash found at %s", RSTRING_PTR(fields_upto_index(fields, i)));
|
80
55
|
}
|
81
56
|
}
|
82
57
|
|
@@ -92,9 +67,11 @@ static inline void parse_fields(VALUE args, VALUE *fields) {
|
|
92
67
|
}
|
93
68
|
|
94
69
|
static inline VALUE fields_upto_index(VALUE fields, long index) {
|
95
|
-
|
96
|
-
|
97
|
-
|
70
|
+
char error_message_pointer[compute_error_message_length(fields, index)];
|
71
|
+
|
72
|
+
copy_error_message(fields, index, error_message_pointer);
|
73
|
+
|
74
|
+
return rb_str_new2(error_message_pointer);
|
98
75
|
}
|
99
76
|
|
100
77
|
static inline void parse_singleton_args(int argc, VALUE *argv, VALUE *hash, VALUE *fields) {
|
@@ -108,3 +85,48 @@ static inline void parse_method_args(int argc, VALUE *argv, VALUE *fields) {
|
|
108
85
|
rb_scan_args(argc, argv, "*", &args);
|
109
86
|
parse_fields(args, fields);
|
110
87
|
}
|
88
|
+
|
89
|
+
static inline long compute_error_message_length(VALUE fields, long index) {
|
90
|
+
long error_length = 0;
|
91
|
+
|
92
|
+
for (long j = 0; j <= index; j++) {
|
93
|
+
VALUE field = RARRAY_AREF(fields, j);
|
94
|
+
|
95
|
+
if (TYPE(field) == T_SYMBOL) {
|
96
|
+
error_length += rb_str_length(rb_id2str(SYM2ID(field)));
|
97
|
+
} else {
|
98
|
+
error_length += RSTRING_LEN(field);
|
99
|
+
}
|
100
|
+
|
101
|
+
if (j != index) {
|
102
|
+
error_length++;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
return error_length;
|
107
|
+
}
|
108
|
+
|
109
|
+
static inline void copy_error_message(VALUE fields, long index, char* error_message_pointer) {
|
110
|
+
char* current_char_pointer = error_message_pointer;
|
111
|
+
|
112
|
+
for (long j = 0; j <= index; j++) {
|
113
|
+
VALUE field = RARRAY_AREF(fields, j);
|
114
|
+
|
115
|
+
long size;
|
116
|
+
if (TYPE(field) == T_SYMBOL) {
|
117
|
+
size = rb_str_length(rb_id2str(SYM2ID(field)));
|
118
|
+
memcpy(current_char_pointer, RSTRING_PTR(rb_id2str(SYM2ID(field))), size);
|
119
|
+
} else {
|
120
|
+
size = RSTRING_LEN(field);
|
121
|
+
memcpy(current_char_pointer, RSTRING_PTR(field), size);
|
122
|
+
}
|
123
|
+
|
124
|
+
current_char_pointer += size;
|
125
|
+
|
126
|
+
if (j != index) {
|
127
|
+
memcpy(current_char_pointer, ".", 1);
|
128
|
+
current_char_pointer++;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
data/fat.gemspec
CHANGED
data/test/fat_test.rb
CHANGED
@@ -13,11 +13,9 @@ scope do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
test "honor key type" do |hash|
|
16
|
-
|
17
|
-
assert_raise { Fat.fetch_at(hash, "foo", :not, :found) }
|
16
|
+
assert_raise(Fat::FatError) { Fat.at(hash, :foo, :not, :found) }
|
18
17
|
|
19
18
|
assert_equal :found, Fat.at(hash, :foo, "bar", :baz)
|
20
|
-
assert_equal :found, Fat.fetch_at(hash, :foo, "bar", :baz)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
@@ -33,11 +31,9 @@ scope do
|
|
33
31
|
end
|
34
32
|
|
35
33
|
test "namespaced string keys" do |hash|
|
36
|
-
|
37
|
-
assert_raise { Fat.fetch_at(hash, "foo.not.found") }
|
34
|
+
assert_raise(Fat::FatError) { Fat.at(hash, "foo", :not, :found) }
|
38
35
|
|
39
36
|
assert_equal :found, Fat.at(hash, "foo.bar.baz")
|
40
|
-
assert_equal :found, Fat.fetch_at(hash, "foo.bar.baz")
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
@@ -56,15 +52,11 @@ scope do
|
|
56
52
|
|
57
53
|
test "include the module" do |hash|
|
58
54
|
assert hash.respond_to?(:at)
|
59
|
-
assert hash.respond_to?(:fetch_at)
|
60
55
|
end
|
61
56
|
|
62
57
|
test "honor Fat interface" do |hash|
|
63
58
|
assert_equal :found, hash.at("foo", "bar", "baz")
|
64
59
|
assert_equal :found, hash.at("foo.bar.baz")
|
65
|
-
|
66
|
-
assert_equal :found, hash.fetch_at("foo", "bar", "baz")
|
67
|
-
assert_equal :found, hash.fetch_at("foo.bar.baz")
|
68
60
|
end
|
69
61
|
end
|
70
62
|
|
@@ -80,9 +72,19 @@ scope do
|
|
80
72
|
}
|
81
73
|
end
|
82
74
|
|
83
|
-
test "
|
84
|
-
|
85
|
-
|
75
|
+
test "raise error if a value is not a hash" do |hash|
|
76
|
+
assert_raise(Fat::FatError) { Fat.at(hash, "foo.not_a_hash.baz") }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
scope do
|
81
|
+
setup do
|
82
|
+
Hash.include(Fat)
|
83
|
+
end
|
84
|
+
|
85
|
+
test "corner case" do
|
86
|
+
assert_raise(Fat::FatError) { {}.at(:foo, :bar) }
|
87
|
+
assert_raise(Fat::FatError) { Fat.at({}, :foo, :bar) }
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
data/test/memleak.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require_relative "../lib/fat"
|
3
|
+
|
4
|
+
# The point of this test is to check for memory leaks.
|
5
|
+
# The idea is to run the test suite forever and monitor the process with `htop`
|
6
|
+
# to see if the memory consumption increases.
|
7
|
+
|
8
|
+
loop do
|
9
|
+
Cutest.run_file(File.absolute_path("test/fat_test.rb"))
|
10
|
+
end
|
11
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Tolchinsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cutest
|
@@ -58,6 +58,7 @@ files:
|
|
58
58
|
- fat.gemspec
|
59
59
|
- lib/.gitkeep
|
60
60
|
- test/fat_test.rb
|
61
|
+
- test/memleak.rb
|
61
62
|
homepage: https://github.com/tonchis/fat
|
62
63
|
licenses:
|
63
64
|
- MIT
|