From 3731d9ba00ff1602c0103f3b1b2242f9faa17d99 Mon Sep 17 00:00:00 2001
From: Masafumi Yokoyama <yokoyama@clear-code.com>
Date: Sat, 5 Mar 2016 17:59:29 +0900
Subject: [PATCH] Add Groonga::Config#each

To bind the following functions:

  * grn_config_cursor_open()
  * grn_config_cursor_next()
  * grn_config_cursor_get_key()
  * grn_config_cursor_get_value()

GitHub: #116
---
 ext/groonga/rb-grn-config.c | 57 +++++++++++++++++++++++++++++++++++++
 test/test-config.rb         | 10 +++++++
 2 files changed, 67 insertions(+)

diff --git a/ext/groonga/rb-grn-config.c b/ext/groonga/rb-grn-config.c
index 70ec142e..32719a2a 100644
--- a/ext/groonga/rb-grn-config.c
+++ b/ext/groonga/rb-grn-config.c
@@ -166,6 +166,61 @@ rb_grn_config_delete (VALUE self, VALUE rb_key)
     return Qnil;
 }
 
+/*
+ * Passes all key/value of the config to block in order.
+ *
+ * @example Shows all keys and all values of the config
+ *   context.config["rroonga.key1"] = "value1"
+ *   context.config["rroonga.key2"] = "value2"
+ *   keys = []
+ *   values = []
+ *   context.config.each do |key, value|
+ *     keys << key
+ *     values << value
+ *   end
+ *   p keys   # => ["rroonga.key1", "rroonga.key2"]
+ *   p values # => ["value1", "value2"]
+ *
+ * @overload each
+ *   @yield [key, value]
+ *
+ * @since 6.0.0
+ */
+static VALUE
+rb_grn_config_each (VALUE self)
+{
+    grn_ctx *context = NULL;
+    grn_obj *cursor;
+    VALUE rb_context;
+
+    RETURN_ENUMERATOR(self, 0, NULL);
+
+    rb_context = rb_iv_get(self, "@context");
+    context = rb_grn_context_ensure(&rb_context);
+
+    cursor = grn_config_cursor_open(context);
+    while (grn_config_cursor_next(context, cursor) == GRN_TRUE) {
+        const char *key;
+        uint32_t key_size;
+        const char *value;
+        uint32_t value_size;
+        VALUE rb_key, rb_value, rb_key_value;
+
+        key_size = grn_config_cursor_get_key(context, cursor, &key);
+        rb_key = rb_str_new(key, key_size);
+        value_size = grn_config_cursor_get_value(context, cursor, &value);
+        rb_value = rb_str_new(value, value_size);
+
+        rb_key_value = rb_ary_new();
+        rb_ary_push(rb_key_value, rb_key);
+        rb_ary_push(rb_key_value, rb_value);
+
+        rb_yield(rb_key_value);
+    }
+
+    return Qnil;
+}
+
 void
 rb_grn_init_config (VALUE mGrn)
 {
@@ -180,4 +235,6 @@ rb_grn_init_config (VALUE mGrn)
     rb_define_method(cGrnConfig, "[]=", rb_grn_config_set, 2);
 
     rb_define_method(cGrnConfig, "delete", rb_grn_config_delete, 1);
+
+    rb_define_method(cGrnConfig, "each", rb_grn_config_each, 0);
 }
diff --git a/test/test-config.rb b/test/test-config.rb
index 94ab4f87..a7dce342 100644
--- a/test/test-config.rb
+++ b/test/test-config.rb
@@ -36,4 +36,14 @@ class ConfigTest < Test::Unit::TestCase
     context.config.delete("rroonga.key")
     assert_nil(context.config["rroonga.key"])
   end
+
+  test "#each" do
+    context.config["rroonga.key1"] = "value1"
+    context.config["rroonga.key2"] = "value2"
+    assert_equal([
+                    ["rroonga.key1", "value1"],
+                    ["rroonga.key2", "value2"],
+                 ],
+                 context.config.each.to_a)
+  end
 end