fat 1.0.0 → 2.0.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/README.md +16 -13
- data/ext/fat/fat.c +25 -48
- data/fat.gemspec +1 -1
- data/test/fat_test.rb +27 -55
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64e614f67b483c476550fd7b144dbb8e03ae010c
|
4
|
+
data.tar.gz: 0f4ace7f1c241251e27eaa37eccba1d9e29cdef6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e5bee3db122f8470ec1403273f72274192affdb2479d129798447d4535420f555f49e7fe6997830e59828686f5f4922913e83803fef0e89aeb680cbfdf55156
|
7
|
+
data.tar.gz: 8e0241de729d4daa1f03711c519b73929dfdbcf14dafef0d282a10b2ead4181509081dd34de380e9a4eb8db6f589fef18fbe0fd4974b4f9a9d552d532a59f936
|
data/README.md
CHANGED
@@ -19,9 +19,9 @@ hash = {
|
|
19
19
|
}
|
20
20
|
```
|
21
21
|
|
22
|
-
To get your `:value` you usually do `hash["foo"]["bar"]["baz"]`. But what happens if `"bar"` doesn't exist? Yeap, BOOM!
|
22
|
+
To get your `:value` you usually do `hash["foo"]["bar"]["baz"]`. But what happens if `"bar"` doesn't exist? Yeap, BOOM! You will get an `undefined method [] for nil` error.
|
23
23
|
|
24
|
-
|
24
|
+
Using `Fat` you can walk the hash up to the `:value`, but it'll raise an exception if it finds `nil` at any point.
|
25
25
|
|
26
26
|
```ruby
|
27
27
|
require "fat"
|
@@ -30,10 +30,22 @@ Fat.at(hash, "foo", "bar", "baz")
|
|
30
30
|
# => :value
|
31
31
|
|
32
32
|
Fat.at(hash, "foo", "not", "here")
|
33
|
-
# => Fat::FatError:
|
33
|
+
# => Fat::FatError: foo.not is nil
|
34
|
+
|
35
|
+
Fat.at(hash, "foo", "bar", "nope")
|
36
|
+
# => Fat::FatError: foo.bar.nope is nil
|
37
|
+
```
|
38
|
+
|
39
|
+
You can specify a default return value with the `default:` keyword if you don't want an exception raised.
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require "fat"
|
43
|
+
|
44
|
+
Fat.at(hash, "foo", "not", "here", default: "whoops")
|
45
|
+
# => "whoops"
|
34
46
|
```
|
35
47
|
|
36
|
-
|
48
|
+
If you set `default: nil` this method behaves exactly like [Hash#dig](http://ruby-doc.org/core-2.3.0/Hash.html#method-i-dig), available from Ruby 2.3.0.
|
37
49
|
|
38
50
|
It's the same with Symbols
|
39
51
|
|
@@ -59,15 +71,6 @@ hash.at("foo", "bar", "baz")
|
|
59
71
|
# => :value
|
60
72
|
```
|
61
73
|
|
62
|
-
If all your keys are Strings you can *namespace* them with dots.
|
63
|
-
|
64
|
-
```ruby
|
65
|
-
Hash.include(Fat)
|
66
|
-
|
67
|
-
hash.at("foo.bar.baz")
|
68
|
-
# => :value
|
69
|
-
```
|
70
|
-
|
71
74
|
# Install
|
72
75
|
|
73
76
|
```bash
|
data/ext/fat/fat.c
CHANGED
@@ -9,17 +9,14 @@ void Init_fat();
|
|
9
9
|
static VALUE singleton_method_at(int argc, VALUE *argv, VALUE self);
|
10
10
|
static VALUE method_at(int argc, VALUE *argv, VALUE hash);
|
11
11
|
|
12
|
-
static VALUE fat(VALUE hash, VALUE fields);
|
12
|
+
static VALUE fat(VALUE hash, VALUE fields, VALUE default_value);
|
13
13
|
|
14
14
|
// Helpers
|
15
|
-
static inline void parse_fields(VALUE args, VALUE *fields);
|
16
15
|
static inline VALUE fields_upto_index(VALUE fields, long index);
|
17
|
-
static inline void parse_singleton_args(int argc, VALUE *argv, VALUE *hash, VALUE *fields);
|
18
|
-
static inline void parse_method_args(int argc, VALUE *argv, VALUE *fields);
|
19
16
|
static inline long compute_error_message_length(VALUE fields, long index);
|
20
17
|
static inline void copy_error_message(VALUE fields, long index, char* error_message_pointer);
|
21
|
-
static inline VALUE str_to_sym(VALUE str);
|
22
18
|
static inline VALUE sym_to_str(VALUE sym);
|
19
|
+
static inline VALUE extract_default(VALUE keywords);
|
23
20
|
|
24
21
|
void Init_fat(void) {
|
25
22
|
Fat = rb_define_module("Fat");
|
@@ -32,52 +29,38 @@ void Init_fat(void) {
|
|
32
29
|
static VALUE singleton_method_at(int argc, VALUE *argv, VALUE self) {
|
33
30
|
VALUE hash;
|
34
31
|
VALUE fields;
|
32
|
+
VALUE keywords;
|
35
33
|
|
36
|
-
|
34
|
+
rb_scan_args(argc, argv, "1*:", &hash, &fields, &keywords);
|
37
35
|
|
38
|
-
return fat(hash, fields);
|
36
|
+
return fat(hash, fields, extract_default(keywords));
|
39
37
|
}
|
40
38
|
|
41
39
|
static VALUE method_at(int argc, VALUE *argv, VALUE hash) {
|
42
40
|
VALUE fields;
|
41
|
+
VALUE keywords;
|
43
42
|
|
44
|
-
|
43
|
+
rb_scan_args(argc, argv, "*:", &fields, &keywords);
|
45
44
|
|
46
|
-
return fat(hash, fields);
|
45
|
+
return fat(hash, fields, extract_default(keywords));
|
47
46
|
}
|
48
47
|
|
49
|
-
static VALUE fat(VALUE hash, VALUE fields) {
|
48
|
+
static VALUE fat(VALUE hash, VALUE fields, VALUE default_value) {
|
50
49
|
VALUE value = hash;
|
51
50
|
|
52
51
|
for (long i = 0; i < RARRAY_LEN(fields); i++) {
|
53
52
|
value = rb_hash_aref(value, RARRAY_AREF(fields, i));
|
54
53
|
|
55
|
-
if (
|
56
|
-
|
57
|
-
|
58
|
-
}
|
59
|
-
|
60
|
-
return value;
|
61
|
-
}
|
62
|
-
|
63
|
-
static inline void parse_fields(VALUE args, VALUE *fields) {
|
64
|
-
if (RARRAY_LEN(args) == 1) {
|
65
|
-
*fields = rb_str_split(RARRAY_PTR(args)[0], ".");
|
66
|
-
|
67
|
-
if (RARRAY_LEN(*fields) == 1) {
|
68
|
-
VALUE split = rb_str_split(RARRAY_PTR(args)[0], ":");
|
69
|
-
|
70
|
-
if (RARRAY_LEN(split) == 1) {
|
71
|
-
rb_raise(rb_eFatError, "Single argument expected to be a namespace with dots (.) or colons (:)");
|
54
|
+
if (NIL_P(value)) {
|
55
|
+
if (!NIL_P(default_value)) {
|
56
|
+
return default_value;
|
72
57
|
} else {
|
73
|
-
|
74
|
-
rb_ary_store(*fields, i, str_to_sym(RARRAY_AREF(split, i)));
|
75
|
-
}
|
58
|
+
rb_raise(rb_eFatError, "%s is nil", RSTRING_PTR(fields_upto_index(fields, i)));
|
76
59
|
}
|
77
60
|
}
|
78
|
-
} else {
|
79
|
-
*fields = args;
|
80
61
|
}
|
62
|
+
|
63
|
+
return value;
|
81
64
|
}
|
82
65
|
|
83
66
|
static inline VALUE fields_upto_index(VALUE fields, long index) {
|
@@ -88,18 +71,6 @@ static inline VALUE fields_upto_index(VALUE fields, long index) {
|
|
88
71
|
return rb_str_new2(error_message_pointer);
|
89
72
|
}
|
90
73
|
|
91
|
-
static inline void parse_singleton_args(int argc, VALUE *argv, VALUE *hash, VALUE *fields) {
|
92
|
-
VALUE args;
|
93
|
-
rb_scan_args(argc, argv, "1*", hash, &args);
|
94
|
-
parse_fields(args, fields);
|
95
|
-
}
|
96
|
-
|
97
|
-
static inline void parse_method_args(int argc, VALUE *argv, VALUE *fields) {
|
98
|
-
VALUE args;
|
99
|
-
rb_scan_args(argc, argv, "*", &args);
|
100
|
-
parse_fields(args, fields);
|
101
|
-
}
|
102
|
-
|
103
74
|
static inline long compute_error_message_length(VALUE fields, long index) {
|
104
75
|
long error_length = 0;
|
105
76
|
|
@@ -150,10 +121,16 @@ static inline void copy_error_message(VALUE fields, long index, char* error_mess
|
|
150
121
|
*current_char_pointer++ = '\0';
|
151
122
|
}
|
152
123
|
|
153
|
-
static inline VALUE str_to_sym(VALUE str) {
|
154
|
-
return ID2SYM(rb_intern(RSTRING_PTR(str)));
|
155
|
-
}
|
156
|
-
|
157
124
|
static inline VALUE sym_to_str(VALUE sym) {
|
158
125
|
return rb_id2str(SYM2ID(sym));
|
159
126
|
}
|
127
|
+
|
128
|
+
static inline VALUE extract_default(VALUE keywords) {
|
129
|
+
VALUE result = Qnil;
|
130
|
+
|
131
|
+
if (!NIL_P(keywords)) {
|
132
|
+
result = rb_hash_aref(keywords, ID2SYM(rb_intern("default")));
|
133
|
+
}
|
134
|
+
|
135
|
+
return result;
|
136
|
+
}
|
data/fat.gemspec
CHANGED
data/test/fat_test.rb
CHANGED
@@ -1,32 +1,6 @@
|
|
1
1
|
require "cutest"
|
2
2
|
require_relative "../lib/fat"
|
3
3
|
|
4
|
-
scope do
|
5
|
-
setup do
|
6
|
-
{
|
7
|
-
foo: {
|
8
|
-
"bar" => {
|
9
|
-
baz: :found
|
10
|
-
}
|
11
|
-
}
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
test "honor key type" do |hash|
|
16
|
-
exception = assert_raise(Fat::FatError) { Fat.at(hash, :foo, :bar, :found) }
|
17
|
-
assert_equal "No hash found at foo.bar", exception.message
|
18
|
-
|
19
|
-
assert_equal :found, Fat.at(hash, :foo, "bar", :baz)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
scope do
|
24
|
-
test "single argument must be a namespace" do
|
25
|
-
exception = assert_raise(Fat::FatError) { Fat.at({"foo" => "bar"}, "foo") }
|
26
|
-
assert_equal "Single argument expected to be a namespace with dots (.) or colons (:)", exception.message
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
4
|
scope do
|
31
5
|
setup do
|
32
6
|
{
|
@@ -38,53 +12,47 @@ scope do
|
|
38
12
|
}
|
39
13
|
end
|
40
14
|
|
41
|
-
test "
|
42
|
-
assert_equal :found, Fat.at(hash, "foo
|
15
|
+
test "find value" do |hash|
|
16
|
+
assert_equal :found, Fat.at(hash, "foo", "bar", "baz")
|
17
|
+
end
|
18
|
+
|
19
|
+
test "don't find value" do |hash|
|
20
|
+
exception = assert_raise(Fat::FatError) { Fat.at(hash, "foo", "bar", "wat") }
|
21
|
+
assert_equal "foo.bar.wat is nil", exception.message
|
43
22
|
|
44
|
-
exception = assert_raise(Fat::FatError) { Fat.at(hash, "foo
|
45
|
-
assert_equal "
|
23
|
+
exception = assert_raise(Fat::FatError) { Fat.at(hash, "foo", "wat", "baz") }
|
24
|
+
assert_equal "foo.wat is nil", exception.message
|
46
25
|
end
|
47
|
-
end
|
48
26
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
foo: {
|
53
|
-
bar: {
|
54
|
-
baz: :found
|
55
|
-
}
|
56
|
-
}
|
57
|
-
}
|
27
|
+
test "return default value" do |hash|
|
28
|
+
assert_equal "default", Fat.at(hash, "foo", "wat", "baz", default: "default")
|
29
|
+
assert_equal "default", Fat.at(hash, "foo", "bar", "wat", default: "default")
|
58
30
|
end
|
59
31
|
|
60
|
-
test "
|
61
|
-
|
32
|
+
test "include the module" do |hash|
|
33
|
+
Hash.include(Fat)
|
62
34
|
|
63
|
-
|
64
|
-
assert_equal
|
35
|
+
assert hash.respond_to?(:at)
|
36
|
+
assert_equal :found, hash.at("foo", "bar", "baz")
|
65
37
|
end
|
66
38
|
end
|
67
39
|
|
68
40
|
scope do
|
69
41
|
setup do
|
70
|
-
Hash.include(Fat)
|
71
|
-
|
72
42
|
{
|
73
|
-
|
43
|
+
foo: {
|
74
44
|
"bar" => {
|
75
|
-
|
45
|
+
baz: :found
|
76
46
|
}
|
77
47
|
}
|
78
48
|
}
|
79
49
|
end
|
80
50
|
|
81
|
-
test "
|
82
|
-
|
83
|
-
|
51
|
+
test "honor key type" do |hash|
|
52
|
+
exception = assert_raise(Fat::FatError) { Fat.at(hash, :foo, :bar, :found) }
|
53
|
+
assert_equal "foo.bar is nil", exception.message
|
84
54
|
|
85
|
-
|
86
|
-
assert_equal :found, hash.at("foo", "bar", "baz")
|
87
|
-
assert_equal :found, hash.at("foo.bar.baz")
|
55
|
+
assert_equal :found, Fat.at(hash, :foo, "bar", :baz)
|
88
56
|
end
|
89
57
|
end
|
90
58
|
|
@@ -93,9 +61,13 @@ scope do
|
|
93
61
|
Hash.include(Fat)
|
94
62
|
end
|
95
63
|
|
96
|
-
test "corner case" do
|
64
|
+
test "corner case: empty hashes" do
|
97
65
|
assert_raise(Fat::FatError) { {}.at(:foo, :bar) }
|
98
66
|
assert_raise(Fat::FatError) { Fat.at({}, :foo, :bar) }
|
99
67
|
end
|
68
|
+
|
69
|
+
test "corner case: lookup a single key" do
|
70
|
+
assert_equal :found, {"foo" => :found}.at("foo")
|
71
|
+
end
|
100
72
|
end
|
101
73
|
|
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:
|
4
|
+
version: 2.0.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:
|
11
|
+
date: 2015-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cutest
|