fat 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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