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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -13
  3. data/ext/fat/fat.c +25 -48
  4. data/fat.gemspec +1 -1
  5. data/test/fat_test.rb +27 -55
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e695c0dfe4c5034dc2977e30ba13260f4359fcc
4
- data.tar.gz: 92c736aa2db12b4f06926cf5d3065c56720b317d
3
+ metadata.gz: 64e614f67b483c476550fd7b144dbb8e03ae010c
4
+ data.tar.gz: 0f4ace7f1c241251e27eaa37eccba1d9e29cdef6
5
5
  SHA512:
6
- metadata.gz: a8595a6a2bf6701c6b9477d79eb875cbe05c70b2db92ce76a1b1f0475da22bfb93dd8f904a7944f35e30015c3a93edfb51677ee9be4ef9c194ccef53c78bd495
7
- data.tar.gz: b2cc74a7a6015d67b172ca55a3eaf25beda14b4129309e8a9dfe88b5c5f69f29330aa5848576a2a786b39e24490c8ca79bd5d146a43e9ed0c7fa6f5fa0794d91
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
- 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.
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: No hash found at foo.not
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
- 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`.
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
@@ -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
- parse_singleton_args(argc, argv, &hash, &fields);
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
- parse_method_args(argc, argv, &fields);
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 (i < RARRAY_LEN(fields) - 1 && TYPE(value) != T_HASH) {
56
- rb_raise(rb_eFatError, "No hash found at %s", RSTRING_PTR(fields_upto_index(fields, i)));
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
- for (long i = 0; i < RARRAY_LEN(split); i++) {
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
+ }
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "fat"
3
- s.version = "1.0.0"
3
+ s.version = "2.0.0"
4
4
  s.summary = "C extension to find values in nested hashes without pain"
5
5
  s.description = s.summary
6
6
  s.authors = ["Lucas Tolchinsky"]
@@ -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 "namespaced strings" do |hash|
42
- assert_equal :found, Fat.at(hash, "foo.bar.baz")
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.not.baz") }
45
- assert_equal "No hash found at foo.not", exception.message
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
- scope do
50
- setup do
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 "namespaced symbols" do |hash|
61
- assert_equal :found, Fat.at(hash, "foo:bar:baz")
32
+ test "include the module" do |hash|
33
+ Hash.include(Fat)
62
34
 
63
- exception = assert_raise(Fat::FatError) { Fat.at(hash, "foo:not:baz") }
64
- assert_equal "No hash found at foo.not", exception.message
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
- "foo" => {
43
+ foo: {
74
44
  "bar" => {
75
- "baz" => :found
45
+ baz: :found
76
46
  }
77
47
  }
78
48
  }
79
49
  end
80
50
 
81
- test "include the module" do |hash|
82
- assert hash.respond_to?(:at)
83
- end
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
- test "honor Fat interface" do |hash|
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: 1.0.0
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: 2014-11-06 00:00:00.000000000 Z
11
+ date: 2015-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cutest