#50 - documentation using RDoc
authorFedor Yudanov <fedwiz@academ.org>
Wed, 17 Apr 2013 06:25:35 +0000 (13:25 +0700)
committerFedor Yudanov <fedwiz@academ.org>
Wed, 17 Apr 2013 06:25:35 +0000 (13:25 +0700)
rbejdb/src/rbejdb.c
rbejdb/test/t1.rb
rbejdb/test/t2.rb

index 7981200..485c6aa 100644 (file)
  *  Boston, MA 02111-1307 USA.
  *************************************************************************************************/
 
+/*
+ * Document-class: EJDB
+ *    Main EJDB class that contains all database control methods. Instance should be created by EJDB::open
+ */
+
+
 #include <tcejdb/ejdb_private.h>
 #include <ruby.h>
 
@@ -71,13 +77,13 @@ VALUE get_hash_option(VALUE hash, const char* opt) {
     return !NIL_P(res) ? res : rb_hash_aref(hash, rb_str_new2(opt));
 }
 
-static int raise_ejdb_error(EJDB *ejdb) {
+int raise_ejdb_error(EJDB *ejdb) {
     int ecode = ejdbecode(ejdb);
     const char *emsg = ejdberrmsg(ecode);
     rb_raise(rb_eRuntimeError, "%s", emsg);
 }
 
-static int nil_or_raise_ejdb_error(EJDB *ejdb) {
+int nil_or_raise_ejdb_error(EJDB *ejdb) {
     int ecode = ejdbecode(ejdb);
     if (ecode != TCESUCCESS && ecode != TCENOREC) {
         raise_ejdb_error(ejdb);
@@ -108,6 +114,22 @@ void EJDB_free(RBEJDB* rejdb) {
 }
 
 
+/*
+ * call-seq:
+ *   EJDB::open(path, mode) -> EJDB
+ *
+ * Open database. Return database instance handle object. <br/>
+ * Default open mode: JBOWRITER | JBOCREAT .
+ *
+ * - +path+ (String) - database main file name
+ * - +mode+ (Number) - bitmask of open modes:
+ *
+ * [JBOREADER] Open as a reader.
+ * [JBOWRITER] Open as a writer.
+ * [JBOCREAT] Create if db file not exists
+ * [JBOTRUNC] Truncate db.
+ *
+ */
 VALUE EJDB_open(VALUE clazz, VALUE path, VALUE mode) {
     SafeStringValue(path);
     Check_Type(mode, T_FIXNUM);
@@ -129,29 +151,44 @@ VALUE EJDB_open(VALUE clazz, VALUE path, VALUE mode) {
     return ejdbWrap;
 }
 
+/*
+ * call-seq:
+ *   ejdb.open? -> true|false
+ *
+ * Check if database in opened state.
+ */
 VALUE EJDB_is_open(VALUE self) {
     EJDB* ejdb = getEJDB(self);
     return ejdb && ejdbisopen(ejdb) ? Qtrue : Qfalse;
 }
 
-void EJDB_close(VALUE self) {
-    ejdbclose(getEJDB(self));
-}
-
-void EJDB_drop_collection(int argc, VALUE* argv, VALUE self) {
-    VALUE collName;
-    VALUE prune;
-
-    rb_scan_args(argc, argv, "11", &collName, &prune);
-    SafeStringValue(collName);
 
-    EJDB* ejdb = getEJDB(self);
-    if (!ejdbrmcoll(ejdb, StringValuePtr(collName), RTEST(prune))) {
-        raise_ejdb_error(ejdb);        
-    }
+/*
+ * call-seq:
+ *   ejdb.close -> nil
+ *
+ * Close database.<br/>
+ * If database was not opened it does nothing.
+ */
+VALUE EJDB_close(VALUE self) {
+    ejdbclose(getEJDB(self));
+    return Qnil;
 }
 
-void EJDB_ensure_collection(int argc, VALUE* argv, VALUE self) {
+/*
+ * call-seq:
+ *   ejdb.ensure_collection(collName, [copts]) -> nil
+ *
+ * Automatically creates new collection if it does't exists. Collection options +copts+ applied only for newly created collection.<br/>
+ * For existing collections +copts+ takes no effect. <br/>
+ *
+ * Collection options (copts): <br/>
+ * [:cachedrecords] Max number of cached records in shared memory segment. Default: 0
+ * [:records] Estimated number of records in this collection. Default: 65535.
+ * [:large] Specifies that the size of the database can be larger than 2GB. Default: false
+ * [:compressed] If true collection records will be compressed with DEFLATE compression. Default: false.
+ */
+VALUE EJDB_ensure_collection(int argc, VALUE* argv, VALUE self) {
     VALUE collName;
     VALUE copts;
 
@@ -179,8 +216,50 @@ void EJDB_ensure_collection(int argc, VALUE* argv, VALUE self) {
     if (!ejdbcreatecoll(ejdb, StringValuePtr(collName), &jcopts)) {
         raise_ejdb_error(ejdb);
     }
+    return Qnil;
+}
+
+/*
+ * call-seq:
+ *   ejdb.drop_collection(collName, [prune=false]) -> nil
+ *
+ * Drop collection.
+ * - +collName+ (String) - name of collection
+ * - +prune+ (true|false) - if true the collection data will be erased from disk.
+ */
+VALUE EJDB_drop_collection(int argc, VALUE* argv, VALUE self) {
+    VALUE collName;
+    VALUE prune;
+
+    rb_scan_args(argc, argv, "11", &collName, &prune);
+    SafeStringValue(collName);
+
+    EJDB* ejdb = getEJDB(self);
+    if (!ejdbrmcoll(ejdb, StringValuePtr(collName), RTEST(prune))) {
+        raise_ejdb_error(ejdb);        
+    }
+    return Qnil;
 }
 
+/*
+ * call-seq:
+ *   ejdb.save(collName, [obj1, …, objN, merge = false]) -> Array or Number or nil
+ *
+ * Save/update specified hashes or Ruby objects in the collection. If collection with +collName+ does not exists it will be created. <br/>
+ * Each persistent object has unique identifier (OID) placed in the _id property. If a saved object does not have _id it will be autogenerated.
+ * To identify and update object it should contains _id property.<br/><br/>
+ *
+ * NOTE: Field names of passed objects may not contain $ and . characters, error condition will be fired in this case.
+ * - +collName+ (String) - name of collection
+ * - +obj+ (Hash or Object) - zero or more objects to save
+ * - +merge+ (Hash or Object) - if true a saved objects will be merged with who's
+ *
+ * <br/>
+ * Returns:
+ * - nil, if no objects provided in arguments
+ * - oid of saved object, as String if one object provided in arguments
+ * - array of oids, in other case
+ */
 VALUE EJDB_save(int argc, VALUE *argv, VALUE self) {
     if (argc < 1) {
         rb_raise(rb_eArgError, "Error calling EJDB.save(): need to specify collection name");
@@ -240,8 +319,23 @@ VALUE EJDB_save(int argc, VALUE *argv, VALUE self) {
     }
 }
 
+/*
+ * call-seq:
+ *   ejdb.load(collName, oid) -> Hash or nil
+ *
+ * Loads JSON object identified by OID from the collection.
+ *
+ * - +collName+ (String) - name of collection
+ * - +oid+ (String) - object identifier (OID)
+ *
+ * <br/>
+ * Returns:
+ * - BSON object as hash
+ * - nil, if it is not found
+ */
 VALUE EJDB_load(VALUE self, VALUE collName, VALUE rboid) {
     SafeStringValue(collName);
+    SafeStringValue(rboid);
 
     EJDB* ejdb = getEJDB(self);
 
@@ -256,6 +350,33 @@ VALUE EJDB_load(VALUE self, VALUE collName, VALUE rboid) {
     return bs ? bson_to_ruby_ensure_destroy(bs) : nil_or_raise_ejdb_error(ejdb);
 }
 
+/*
+ * call-seq:
+ *   ejdb.remove(collName, oid) -> nil
+ *
+ * Removes JSON object from the collection.
+ *
+ * - +collName+ (String) - name of collection
+ * - +oid+ (String) - object identifier (OID)
+ */
+VALUE EJDB_remove(VALUE self, VALUE collName, VALUE rboid) {
+    SafeStringValue(collName);
+    SafeStringValue(rboid);
+
+    EJDB* ejdb = getEJDB(self);
+
+    EJCOLL *coll = ejdbgetcoll(ejdb, StringValuePtr(collName));
+    if (!coll) {
+        raise_ejdb_error(ejdb);
+    }
+
+    bson_oid_t oid = ruby_to_bson_oid(rboid);
+    if (!ejdbrmbson(coll, &oid)) {
+        raise_ejdb_error(ejdb);
+    }
+    return Qnil;
+}
+
 
 void prepare_query_hint(VALUE res, VALUE hints, char* hint) {
     VALUE val = get_hash_option(hints, hint);
@@ -269,6 +390,7 @@ VALUE prepare_query_hints(VALUE hints) {
     prepare_query_hint(res, hints, "orderby");
     prepare_query_hint(res, hints, "max");
     prepare_query_hint(res, hints, "skip");
+    prepare_query_hint(res, hints, "fields");
     return res;
 }
 
@@ -357,6 +479,99 @@ VALUE EJDB_find_ensure(VALUE queryWrap, VALUE exception) {
     EJDB_remove_query_internal(rbquery);
 }
 
+
+/*
+ * call-seq:
+ *   ejdb.find(collName, [q = {}, orarr = [], hints = {}]) -> nil
+ *
+ * Execute query on collection. EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy.
+ * Both in query and in +hints+ strings or symbols may be used as keys (f. e. "fpath" and :fpath are equal).
+ *  Supported queries:
+ *  - Simple matching of String OR Number OR Array value:
+ *    -   {"fpath" => "val", ...}
+ *  - $not Negate operation.
+ *    -   {"fpath" => {"$not" => val}} //Field not equal to val
+ *    -   {"fpath" => {"$not" => {"$begin" => prefix}}} //Field not begins with val
+ *  - $begin String starts with prefix
+ *    -   {"fpath" => {"$begin" => prefix}}
+ *  - $gt, $gte (>, >=) and $lt, $lte for number types:
+ *    -   {"fpath" => {"$gt" => number}, ...}
+ *  - $bt Between for number types:
+ *    -   {"fpath" => {"$bt" => [num1, num2]}}
+ *  - $in String OR Number OR Array val matches to value in specified array:
+ *    -   {"fpath" => {"$in" => [val1, val2, val3]}}
+ *  - $nin - Not IN
+ *  - $strand String tokens OR String array val matches all tokens in specified array:
+ *    -   {"fpath" => {"$strand" => [val1, val2, val3]}}
+ *  - $stror String tokens OR String array val matches any token in specified array:
+ *    -   {"fpath" => {"$stror" => [val1, val2, val3]}}
+ *  - $exists Field existence matching:
+ *    -   {"fpath" => {"$exists" => true|false}}
+ *  - $icase Case insensitive string matching:
+ *    -  {"fpath" => {"$icase" => "val1"}} //icase matching
+ *    Ignore case matching with "$in" operation:
+ *    -  {"name" => {"$icase" => {"$in" => ["tHéâtre - театр", "heLLo WorlD"]}}}
+ *    For case insensitive matching you can create special type of string index.
+ *  - $elemMatch The $elemMatch operator matches more than one component within an array element.
+ *    -  { array=> { $elemMatch=> { value1 => 1, value2 => { $gt=> 1 } } } }
+ *    Restriction: only one $elemMatch allowed in context of one array field.
+ *
+ *  Queries can be used to update records:
+ *
+ *  - $set Field set operation.
+ *    - {.., "$set" => {"field1" => val1, "fieldN" => valN}}
+ *  - $upsert Atomic upsert. If matching records are found it will be "$set" operation, otherwise new record will be
+ *    inserted with fields specified by argment object.
+ *     - {.., "$upsert" => {"field1" => val1, "fieldN" => valN}}
+ *  - $inc Increment operation. Only number types are supported.
+ *     - {.., "$inc" => {"field1" => number, ...,  "field1" => number}
+ *  - $dropall In-place record removal operation.
+ *     - {.., "$dropall" => true}
+ *  - $addToSet Atomically adds value to the array only if its not in the array already. If containing array is missing it will be created.
+ *     - {.., "$addToSet" => {"fpath" => val1, "fpathN" => valN, ...}}
+ *  - $addToSetAll Batch version if $addToSet
+ *     - {.., "$addToSetAll" => {"fpath" => [array of values to add], ...}}
+ *  - $pull Atomically removes all occurrences of value from field, if field is an array.
+ *     - {.., "$pull" => {"fpath" => val1, "fpathN" => valN, ...}}
+ *  - $pullAll Batch version of $pull
+ *     - {.., "$pullAll" => {"fpath" => [array of values to remove], ...}}
+ *
+ * <br/>
+ * NOTE: It is better to execute update queries with `$onlycount=true` hint flag
+ * or use the special +update+ method to avoid unnecessarily rows fetching.<br/><br/>
+ *
+ * NOTE: Negate operations: $not and $nin not using indexes
+ * so they can be slow in comparison to other matching operations.<br/><br/>
+ *
+ * NOTE: Only one index can be used in search query operation.<br/><br/>
+ *
+ * QUERY HINTS (specified by +hints+ argument):
+ * [:max] Maximum number in the result set
+ * [:skip] Number of skipped results in the result set
+ * [:orderby] Sorting order of query fields.
+ * [:onlycount] If `true` only count of matching records will be returned without placing records in result set.
+ * [:fields] Set subset of fetched fields
+ * If a field presented in $orderby clause it will be forced to include in resulting records.<br/>
+ * Example:<br/>
+ *  hints:    {
+ *    "$orderby" : { //ORDER BY field1 ASC, field2 DESC
+ *        "field1" : 1,
+ *        "field2" : -1
+ *    },
+ *    "$fields" : { //SELECT ONLY {_id, field1, field2}
+ *        "field1" : 1,
+ *        "field2" : 1
+ *    }
+ *  }
+ *
+ * Many C API query examples can be found in `tcejdb/testejdb/t2.c` test case.<br/><br/>
+ *
+ * - +collName+ (String) - name of collection
+ * - +q+ (Hash or Object) - query object. In most cases it will be easier to use hash to specify EJDB queries
+ * - +orarr+ (Array) - array of additional OR query objects (joined with OR predicate). If 3rd argument is not array it
+ * will be recognized as +hints+ argument
+ * - +hints+ (Hash or Object) - object with query hints
+ */
 VALUE EJDB_find(int argc, VALUE* argv, VALUE self) {
     VALUE collName;
     VALUE q;
@@ -415,23 +630,6 @@ void EJDB_update(int argc, VALUE* argv, VALUE self) {
 }
 
 
-void EJDB_remove(VALUE self, VALUE collName, VALUE rboid) {
-    SafeStringValue(collName);
-    SafeStringValue(rboid);
-
-    EJDB* ejdb = getEJDB(self);
-
-    EJCOLL *coll = ejdbgetcoll(ejdb, StringValuePtr(collName));
-    if (!coll) {
-        raise_ejdb_error(ejdb);
-    }
-
-    bson_oid_t oid = ruby_to_bson_oid(rboid);
-    if (!ejdbrmbson(coll, &oid)) {
-        raise_ejdb_error(ejdb);
-    }
-}
-
 
 void EJDB_set_index_internal(VALUE self, VALUE collName, VALUE fpath, int flags) {
     SafeStringValue(collName);
@@ -751,13 +949,14 @@ Init_rbejdb() {
     rb_define_singleton_method(ejdbClass, "new", RUBY_METHOD_FUNC(EJDB_new), 0);
 
     rb_define_const(ejdbClass, "DEFAULT_OPEN_MODE", INT2FIX(DEFAULT_OPEN_MODE));
+    rb_define_const(ejdbClass, "JBOREADER", INT2FIX(JBOREADER));
     rb_define_const(ejdbClass, "JBOWRITER", INT2FIX(JBOWRITER));
     rb_define_const(ejdbClass, "JBOCREAT", INT2FIX(JBOCREAT));
     rb_define_const(ejdbClass, "JBOTSYNC", INT2FIX(JBOTSYNC));
     rb_define_const(ejdbClass, "JBOTRUNC", INT2FIX(JBOTRUNC));
 
     rb_define_singleton_method(ejdbClass, "open", RUBY_METHOD_FUNC(EJDB_open), 2);
-    rb_define_method(ejdbClass, "is_open?", RUBY_METHOD_FUNC(EJDB_is_open), 0);
+    rb_define_method(ejdbClass, "open?", RUBY_METHOD_FUNC(EJDB_is_open), 0);
     rb_define_method(ejdbClass, "close", RUBY_METHOD_FUNC(EJDB_close), 0);
     rb_define_method(ejdbClass, "save", RUBY_METHOD_FUNC(EJDB_save), -1);
     rb_define_method(ejdbClass, "load", RUBY_METHOD_FUNC(EJDB_load), 2);
index cce97fe..670b51a 100644 (file)
@@ -10,7 +10,7 @@ Dir.chdir TESTDB_DIR
 
 ejdb = EJDB.open("zoo", EJDB::DEFAULT_OPEN_MODE)
 
-raise "Failed to open ejdb" unless ejdb.is_open?
+raise "Failed to open ejdb" unless ejdb.open?
 
 ejdb.drop_collection("parrots", true)
 ejdb.drop_collection("cows", true)
@@ -48,6 +48,6 @@ raise "Error querying cows"  unless ejdb.find("cows", :name => "moo").any? {|res
 
 ejdb.close
 
-raise "Failed to close ejdb" unless !ejdb.is_open?
+raise "Failed to close ejdb" unless !ejdb.open?
 
 puts "CONGRATULATIONS!!! Test batch 1 has passed completely!"
index 402f4e1..1e63631 100644 (file)
@@ -16,7 +16,7 @@ $jb = EJDB.open("tdbt2", EJDB::JBOWRITER | EJDB::JBOCREAT | EJDB::JBOTRUNC)
 class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb1_save_load
-    assert $jb.is_open?
+    assert $jb.open?
 
     parrot1 = {
         "name" => "Grenny",
@@ -58,7 +58,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb2_query1
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("parrots", {})
 
@@ -106,7 +106,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb3_query2
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("parrots", {:name => /(grenny|bounty)/i}, {:orderby => {:name => 1}})
 
@@ -130,7 +130,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb4_query3
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("parrots", {}, [{:name => "Grenny"}, {:name => "Bounty"}], {:orderby => {:name => 1}})
 
@@ -163,7 +163,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb5_circular
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     #Circular query object
     cir_query = {}
@@ -209,7 +209,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb6_save_load_buffer
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     sally = {
         "name" => "Sally",
@@ -246,7 +246,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb7_use_string_index
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("birds", {"name" => "Molly"}, {:explain => true})
     assert_not_nil results
@@ -269,7 +269,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb8_cmeta
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     dm = $jb.get_db_meta
     assert dm
@@ -286,7 +286,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdb9_test_update1
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     result = $jb.update("parrots", {"name" => {"$icase" => "GRENNY"}, "$inc" => {"age" => 10}}, {:explain => true})
     assert_equal(1, result.count)
@@ -317,7 +317,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdba_id_nin
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     obj = $jb.find_one("parrots", {})
     assert_not_nil obj
@@ -341,7 +341,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbb_test_remove
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     obj = $jb.find_one("birds", {"name" => "Molly"})
     assert_not_nil obj
@@ -360,7 +360,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbc_sync
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
     $jb.sync
     puts __method__.inspect + " has passed successfull"
   end
@@ -368,7 +368,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbd_remove_colls
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     $jb.drop_collection("birds")
 
@@ -382,7 +382,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbe_tx1
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     obj = {:foo => "bar"}
 
@@ -428,7 +428,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbf_create_collection_on_upsert
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.update("upsertcoll", {:foo => "bar", "$upsert" => {:foo => "bar"}})
     assert_equal(1, results.count)
@@ -442,7 +442,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbg_max_and_skip_hints
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("parrots", {})
     assert_not_equal(1, results.count)
@@ -628,7 +628,7 @@ class EJDBTestUnit < Test::Unit::TestCase
 
   def test_ejdbj_close
     assert_not_nil $jb
-    assert $jb.is_open?
+    assert $jb.open?
 
     results = $jb.find("parrots", {})
     assert_not_nil results
@@ -638,9 +638,9 @@ class EJDBTestUnit < Test::Unit::TestCase
       results.to_a
     }
 
-    $jb.close
+    assert_nil $jb.close
 
-    assert !$jb.is_open?
+    assert !$jb.open?
     puts __method__.inspect + " has passed successfull"
   end