fat 0.0.5 → 0.1.0
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 +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
|