Ruby gem with the code with the convenient methods written on the top of rufus-tokyo. It is extracted from the code I frequently use when using rufus-tokyo to access Tokyo Cabinet. For example, for table, I always add a row with unique id so I always use generate_unique_id method. By wrapping the call to this method, the code that is using the rufus/tokyo doesn't get cluttered. rufus-tokyo is "ffi based ruby library to access Tokyo Cabinet and Tokyo Tyrant" by John Mettraux (Copyright (c) 2009-2010, see http://github.com/jmettraux/rufus-tokyo/blob/master/LICENSE.txt).
1. Preserve the functionality and behaviour of rufus-tokyo. 1-1. No fork of rufus-tokyo. 1-2. This gem doesn't open rufus-tokyo class and add a functionality there. It is developed as a pure wrapper. 1-3. The minimum or no exception handling of its own is done. All the exceptions should directly come from rufus-tokyo, so that usage of this gem doesn't interfere the usage of rufus-tokyo to access Tokyo Cabinet. (Of course, as found necessary, this gem raises its own exception but it should be minimum.) 2. The data are used in Hash format. 2-1. This gem is not meant to be a mapper to Object like Object-Relational Mapping. Comment from the author (tadatoshi): "I'm using Ruby on Rails most of the time. I'm thinking of using DataMapper for all the Object-Relational Mappings when Ruby on Rails 3.0 becomes available. I don't know much about DataMapper for Tokyo Cabinet so I cannot make a comment about it. But at least, I'm thinking of using this gem only when I want to use the data in Hash format."
rufus-tokyo 1.0.1 or above. sudo gem install ffi --source http://rubygems.org sudo gem install rufus-tokyo --source http://rubygems.org When using Ruby installation through Ruby Version Manager (RVM): gem install ffi --source http://rubygems.org gem install rufus-tokyo --source http://rubygems.org
sudo gem install tokyo_wrapper --source http://rubygems.org When using Ruby installation through Ruby Version Manager (RVM): gem install tokyo_wrapper --source http://rubygems.org
1. Requiring 'tokyo_wrapper' in Ruby code. require 'tokyo_wrapper' 2. Tokyo Cabinet table. 2-1. Getting table object. Tokyo Cabinet table stores data in a file with extension .tct e.g. table.tct TokyoWrapper::Table class has three factory methods to create an instance of itself: create_with_create_write_non_blocking_lock(file) Use when table file doesn't exist. This creates the table file with the given name and opens it for writing data. It locks the table. Only non_locking table (instantiated by create_with_read_non_locking) in a different thread can access the table when the instance from this method is in use (i.e. when it's not closed yet). create_with_write_non_blocking_lock(file) Use when table file already exists and you want to write data in it. It locks the table. Only non_locking table (instantiated by create_with_read_non_locking) in a different thread can access the table when the instance from this method is in use (i.e. when it's not closed yet). create_with_read_non_locking(file) Use when table file already exists and you want to read data from it. It doesn't lock the table. 2-2. Closing table. 2-2-1. Table object must be closed when all the operations are done by calling close method. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") ensure write_table.close unless write_table.nil? end 2-2-2. Alternatively, you can pass a block with the operations to a create method. It is ensured that the table is closed. The return value is the return value from the block. e.g. This acts same as the example for 2-2-1. above except that the return value is the last value in the block. TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") do |write_table| end # The table is closed automatically here. 2-3. Basic operations. (Defined in TokyoWrapper::Table class itself.) 2-3-1. Adding data. Call add method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects a hash, which corresponds to one record (row) in a table. It returns the id (pk in rufus/tokyo term) with type integer. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") data_hash = {"street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"} id = write_table.add(data_hash) ensure write_table.close unless write_table.nil? end e.g. This acts exactly same as the example above. id = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") do |write_table| data_hash = {"street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"} write_table.add(data_hash) end # The table is closed automatically here. 2-3-2. Updating data. Call update method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects the id of the table record and a hash of updating data. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" update_data_hash = {"street" => "1112 Main", "notes" => "Recently situation has been changed."} write_table.update(id, update_data_hash) ensure write_table.close unless write_table.nil? end 2-3-3. Deleting data. Call delete method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects the id of the table record. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" write_table.delete(id) ensure write_table.close unless write_table.nil? end 2-3-4. Getting all the records (rows). Call all method on table object with read mode (instantiated by create_with_read_non_locking). It returns an array of hashes. (The same object returned by rufus/tokyo.) e.g. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"}, {:pk => 2, "street" => "2222 Main", "city" => "Quebec", "notes" => "Different notes"}] 2-3-5. Finding a record (rows). Call find method on table object with read mode (instantiated by create_with_read_non_locking). It expects the id of the table record. If :pk_included => true is specified as the last argument (options), returned hash contains :pk (id). (Note: This method is the only one that requires this option. All the other query methods includes :pk by default just like rufus/tokyo.) It returns a hash. e.g.1. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") id = "3" record = read_table.find(id) ensure read_table.close unless read_table.nil? end -> record: {"street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"} e.g.2. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") id = "3" record = read_table.find(id, :pk_included => true) ensure read_table.close unless read_table.nil? end -> record: {:pk => 1, "street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"} 2-4. Query operations. (Defined in TokyoWrapper::TableMethods::Query module and included in TokyoWrapper::Table class.) 2-4-1. Getting all the records (rows) with the given key-value. Call all_by_key_value method on table object with read mode (instantiated by create_with_read_non_locking). It expects key and value. It returns an array of hashes. (The same object returned by rufus/tokyo.) e.g. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all_by_key_value("city", "Montreal") ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes"}] 2-4-2. Getting all the records (rows) with the given multiple key-values. Call all_by_multiple_key_values method on table object with read mode (instantiated by create_with_read_non_locking). It expects a hash of key values. It returns an array of hashes. (The same object returned by rufus/tokyo.) e.g. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all_by_multiple_key_values({"city" => "Montreal", "register_id" => "45"}) ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes", "register_id" => "45"}] 2-5. Associations. (Defined in TokyoWrapper::TableMethods::Associations module and included in TokyoWrapper::Table class.) It is for achieving one-to-many and many-to-many associations. 2-5-1. Adding data with has_many association ids. (A record of the table is associated with multiple records of another table.) Call add method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects a hash with has_many association ids to be array. It returns the id (pk in rufus/tokyo term) with type integer. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") data_hash = {"street" => "1111 Main", "city" => "Montreal", "notes" => "Some notes", "sector_ids" => ["2","5","34","8"]} id = write_table.add(data_hash) ensure write_table.close unless write_table.nil? end 2-5-2. Updating data with has_many association ids. (A record of the table is associated with multiple records of another table.) Call update method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects the id of the table record and a hash of updating data with has_many association ids to be array. e.g. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" update_data_hash = {"street" => "1112 Main", "sector_ids" => ["2","5","40","8","12"]} write_table.update(id, update_data_hash) ensure write_table.close unless write_table.nil? end 2-5-3. Adding has_many association id. (A record of the table is associated with multiple records of another table.) Call add_has_many_association_id method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects the id of the table record, association id name and association id. e.g.1. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" write_table.add_has_many_association_id(id, "sector_id", 78) ensure write_table.close unless write_table.nil? end e.g.2. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" write_table.add_has_many_association_id(id, "sector_id", "78") ensure write_table.close unless write_table.nil? end 2-5-4. Getting all the records (rows) with the given has_many association id. (A record of the table is associated with multiple records of another table.) Call all_by_has_many_association_id method on table object with read mode (instantiated by create_with_read_non_locking). It expects association id name and association id. It returns an array of hashes. Note: If there is another has_many association id, it must to indicated by :keys_for_has_many_association in order to convert its value to array. (See section 2-5-6. for the reason due to rufus/tokyo.) e.g.1. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all_by_has_many_association_id("sector_id", "2") ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "sector_ids" => ["2","5","32","8"]}, {:pk => 2, "street" => "1122 Main", "sector_ids" => ["1","2","3458","9"]}] e.g.2.1. Other has_many association id value is not converted to array by default. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all_by_has_many_association_id("sector_id", "2") ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "sector_ids" => ["2","5","32","8"], "department_ids" => "4,43"}, {:pk => 2, "street" => "1122 Main", "sector_ids" => ["1","2","3458","9"], "department_ids" => "8,5"}] e.g.2.2. Other has_many association id value is converted to array by specifying :keys_for_has_many_association option. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all_by_has_many_association_id("sector_id", "2", :keys_for_has_many_association => ["department_ids"]) ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "sector_ids" => ["2","5","32","8"], "department_ids" => ["4","43"]}, {:pk => 2, "street" => "1122 Main", "sector_ids" => ["1","2","3458","9"], "department_ids" => ["8","5"]}] 2-5-5. Setting a belongs_to association id. (Multiple records of the table is associated with a single record of another table.) Call set_belongs_to_association_id method on table object with write mode (instantiated by create_with_create_write_non_blocking_lock or create_with_write_non_blocking_lock). It expects the id of the table record, association id name and association id. e.g.1. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" write_table.set_belongs_to_association_id(id, "register_id", 78) ensure write_table.close unless write_table.nil? end e.g.2. begin write_table = TokyoWrapper::Table.create_with_create_write_non_blocking_lock("table.txt") id = "3" write_table.set_belongs_to_association_id(id, "register_id", "78") ensure write_table.close unless write_table.nil? end 2-5-6. :keys_for_has_many_association option for query methods. All the query methods accepts :keys_for_has_many_association option as the last argument (options). When it is set, the value for the specified has_many association keys are converted to array in the returned hash. The query methods: all, find, all_by_key_value, and all_by_multiple_key_values This is because has_many association id is stored as a string with ids separated by comma. And simply returning the records from rufus/tokyo keeps that format. Note: When an array is passed as a value to rufus/tokyo, it concatenates the elements of the array without any delimiter and stores the resulting string value. Hence, this gem converts an array to a string with comma separated elements. e.g.1. has_many association id value is not converted to array by default. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "sector_ids" => "2,5,32,8"]}, {:pk => 2, "street" => "1122 Main", "sector_ids" => "1,2,3458,9"]}] e.g.2. has_many association id value is converted to array by specifying :keys_for_has_many_association option. begin read_table = TokyoWrapper::Table.create_with_read_non_locking("table.txt") records = read_table.all(:keys_for_has_many_association => ["sector_ids"]) ensure read_table.close unless read_table.nil? end -> records: [{:pk => 1, "street" => "1111 Main", "sector_ids" => ["2","5","32","8"]}, {:pk => 2, "street" => "1122 Main", "sector_ids" => ["1","2","3458","9"]}]
Copyright © 2010, Tadatoshi Takahashi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.