├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── Rakefile ├── TODO ├── build_config.rb ├── example └── vedis.rb ├── mrbgem.rake ├── src ├── mrb_vedis.c └── mrb_vedis.h └── test └── vedis.rb /.gitignore: -------------------------------------------------------------------------------- 1 | GPATH 2 | GRTAGS 3 | GTAGS 4 | mruby 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | before_install: 5 | - sudo apt-get -qq update 6 | install: 7 | - sudo apt-get -qq install rake bison git gperf 8 | before_script: 9 | - rake test 10 | script: 11 | - rake test 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Redistributions in any form must be accompanied by information on 14 | * how to obtain complete source code for the Vedis engine and any 15 | * accompanying software that uses the Vedis engine software. 16 | * The source code must either be included in the distribution 17 | * or be available for no more than the cost of distribution plus 18 | * a nominal fee, and must be freely redistributable under reasonable 19 | * conditions. For an executable file, complete source code means 20 | * the source code for all modules it contains.It does not include 21 | * source code for modules or files that typically accompany the major 22 | * components of the operating system on which the executable file runs. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS 25 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 | * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 27 | * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS 28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mruby-vedis [![Build Status](https://travis-ci.org/matsumoto-r/mruby-vedis.png?branch=master)](https://travis-ci.org/matsumoto-r/mruby-vedis) 2 | Binding vedis for mruby. 3 | 4 | Vedis is a embeddable datastore and self-contained C library that supports over 70 commands similar to Redis. 5 | 6 | The major difference from Redis is no networking layer since Vedis runs in the same process of the host application. 7 | 8 | Please see the official [Vedis homepage](http://vedis.symisc.net/index.html) for more detail. 9 | 10 | ## Install by mrbgems 11 | 12 | Add the following line to `build_config.rb`: 13 | 14 | ```ruby 15 | MRuby::Build.new do |conf| 16 | 17 | # ... (snip) ... 18 | 19 | conf.gem :github => 'matsumoto-r/mruby-vedis' 20 | end 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```ruby 26 | # In-Memory 27 | v = Vedis.new 28 | 29 | # On-Disk 30 | # v = Vedis.new "/path/to/vedis.db" 31 | 32 | # sym ok 33 | v["test"] = "bbb" 34 | v[:hoge] = 2 35 | v[:foo] = 3 36 | v.set :fuga, "aaa" 37 | 38 | # exec 39 | r1 = v.exec "MSET username james age 27 mail dude@example.com" 40 | r2 = v.exec "MGET username age mail" 41 | 42 | p v["test"] #=> "bbb" 43 | p v[:hoge] #=> "2" 44 | p v["foo"] #=> "3" 45 | p v.get :fuga #=> "aaa" 46 | p r1 #=> nil 47 | p r2 #=> ["james", "27", "dude@example.com"] 48 | ``` 49 | 50 | ## Benchmark comparing Vedis with Redis 51 | 52 | ### Simple benchmark 53 | 54 | ```ruby 55 | class SimpleBenchmark 56 | 57 | def initialize(width = 0) 58 | @width = width 59 | end 60 | 61 | def measure(label) 62 | start = Time.now 63 | yield if block_given? 64 | passed = Time.now - start 65 | 66 | puts "#{make_fixed_label(label)}passed time #{passed} sec" 67 | end 68 | 69 | def make_fixed_label(label) 70 | if @width - label.length > 0 71 | label + ' ' * (@width - label.length) 72 | else 73 | label 74 | end 75 | end 76 | 77 | end 78 | 79 | benchmark = SimpleBenchmark.new 80 | r = Redis.new "127.0.0.1", 6379 81 | v = Vedis.new 82 | n = 100000 83 | 84 | [r, v].each do |kvs| 85 | benchmark.measure("#{kvs.class}: ") do 86 | n.times do |t| 87 | kvs.set t.to_s, t.to_s 88 | end 89 | end 90 | end 91 | 92 | r.close 93 | v.close 94 | ``` 95 | 96 | ### Results 97 | 98 | ```bash 99 | $ mruby test_vedis_redis.rb 100 | Redis: passed time 10.785598 sec 101 | Vedis: passed time 0.06595 sec 102 | ``` 103 | 104 | ## License 105 | 106 | mruby-vedis is licensed under the Sleepycat License, see the LICENSE file. 107 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || "build_config.rb") 2 | 3 | file :mruby do 4 | sh "git clone git://github.com/mruby/mruby.git" 5 | end 6 | 7 | desc "compile binary" 8 | task :compile => :mruby do 9 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all" 10 | end 11 | 12 | desc "test" 13 | task :test => :mruby do 14 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test" 15 | end 16 | 17 | desc "cleanup" 18 | task :clean do 19 | sh "cd mruby && rake deep_clean" 20 | end 21 | 22 | task :default => :test 23 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Support other commands http://vedis.symisc.net/commands.html 2 | -------------------------------------------------------------------------------- /build_config.rb: -------------------------------------------------------------------------------- 1 | MRuby::Build.new do |conf| 2 | # load specific toolchain settings 3 | toolchain :gcc 4 | 5 | # Use mrbgems 6 | # conf.gem 'examples/mrbgems/ruby_extension_example' 7 | # conf.gem 'examples/mrbgems/c_extension_example' do |g| 8 | # g.cc.flags << '-g' # append cflags in this gem 9 | # end 10 | # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' 11 | # conf.gem :github => 'masuidrive/mrbgems-example', :branch => 'master' 12 | # conf.gem :git => 'git@github.com:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v' 13 | 14 | # include the default GEMs 15 | conf.gembox 'default' 16 | conf.gem File.expand_path(File.dirname(__FILE__)) 17 | 18 | # C compiler settings 19 | # conf.cc do |cc| 20 | # cc.command = ENV['CC'] || 'gcc' 21 | # cc.flags = [ENV['CFLAGS'] || %w()] 22 | # cc.include_paths = ["#{root}/include"] 23 | # cc.defines = %w(DISABLE_GEMS) 24 | # cc.option_include_path = '-I%s' 25 | # cc.option_define = '-D%s' 26 | # cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}" 27 | # end 28 | 29 | # mrbc settings 30 | # conf.mrbc do |mrbc| 31 | # mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers 32 | # end 33 | 34 | # Linker settings 35 | # conf.linker do |linker| 36 | # linker.command = ENV['LD'] || 'gcc' 37 | # linker.flags = [ENV['LDFLAGS'] || []] 38 | # linker.flags_before_libraries = [] 39 | # linker.libraries = %w() 40 | # linker.flags_after_libraries = [] 41 | # linker.library_paths = [] 42 | # linker.option_library = '-l%s' 43 | # linker.option_library_path = '-L%s' 44 | # linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}" 45 | # end 46 | 47 | # Archiver settings 48 | # conf.archiver do |archiver| 49 | # archiver.command = ENV['AR'] || 'ar' 50 | # archiver.archive_options = 'rs %{outfile} %{objs}' 51 | # end 52 | 53 | # Parser generator settings 54 | # conf.yacc do |yacc| 55 | # yacc.command = ENV['YACC'] || 'bison' 56 | # yacc.compile_options = '-o %{outfile} %{infile}' 57 | # end 58 | 59 | # gperf settings 60 | # conf.gperf do |gperf| 61 | # gperf.command = 'gperf' 62 | # gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' 63 | # end 64 | 65 | # file extensions 66 | # conf.exts do |exts| 67 | # exts.object = '.o' 68 | # exts.executable = '' # '.exe' if Windows 69 | # exts.library = '.a' 70 | # end 71 | 72 | # file separetor 73 | # conf.file_separator = '/' 74 | end 75 | 76 | # Define cross build settings 77 | # MRuby::CrossBuild.new('32bit') do |conf| 78 | # toolchain :gcc 79 | # 80 | # conf.cc.flags << "-m32" 81 | # conf.linker.flags << "-m32" 82 | # 83 | # conf.build_mrbtest_lib_only 84 | # 85 | # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' 86 | # 87 | # conf.test_runner.command = 'env' 88 | # 89 | # end 90 | -------------------------------------------------------------------------------- /example/vedis.rb: -------------------------------------------------------------------------------- 1 | # In-Memory 2 | v = Vedis.new 3 | 4 | # On-Disk 5 | # v = Vedis.new "/path/to/vedis.db" 6 | 7 | # sym ok 8 | v["test"] = "bbb" 9 | v[:hoge] = 2 10 | v[:foo] = 3 11 | p v << {:foo => "dayo"} 12 | v.set :fuga, "aaa" 13 | 14 | # exec 15 | r1 = v.exec "MSET username james age 27 mail dude@example.com" 16 | r2 = v.exec "MGET username age mail" 17 | 18 | p v["test"] #=> "bbb" 19 | p v[:hoge] #=> "2" 20 | p v["foo"] #=> "3" 21 | p v.get :fuga #=> "aaa" 22 | p r1 #=> nil 23 | p r2 #=> ["james", "27", "dude@example.com"] 24 | 25 | -------------------------------------------------------------------------------- /mrbgem.rake: -------------------------------------------------------------------------------- 1 | MRuby::Gem::Specification.new('mruby-vedis') do |spec| 2 | spec.license = 'Sleepycat License' 3 | spec.authors = 'MATSUMOTO Ryosuke' 4 | spec.version = '0.0.1' 5 | 6 | require 'open3' 7 | 8 | vedis_dir = "#{build_dir}/vedis" 9 | 10 | def run_command env, command 11 | STDOUT.sync = true 12 | puts "build: [exec] #{command}" 13 | Open3.popen2e(env, command) do |stdin, stdout, thread| 14 | print stdout.read 15 | fail "#{command} failed" if thread.value != 0 16 | end 17 | end 18 | 19 | FileUtils.mkdir_p build_dir 20 | 21 | if ! File.exist? vedis_dir 22 | Dir.chdir(build_dir) do 23 | e = {} 24 | run_command e, 'git clone https://github.com/symisc/vedis.git' 25 | end 26 | end 27 | 28 | objs = Dir.glob("#{vedis_dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f| 29 | objfile(f.relative_path_from(vedis_dir).to_s.pathmap("#{vedis_dir}/%X")) 30 | end 31 | 32 | build.libmruby << objs 33 | spec.cc.include_paths << vedis_dir 34 | end 35 | -------------------------------------------------------------------------------- /src/mrb_vedis.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** mrb_vedis - vedis class for mruby using vedis 3 | ** 4 | ** Copyright (c) mod_mruby developers 2013- 5 | ** 6 | ** based on below vedis license. 7 | */ 8 | /* 9 | * Copyright (C) 2013 Symisc Systems, S.U.A.R.L [M.I.A.G Mrad Chems Eddine ]. 10 | * All rights reserved. 11 | * 12 | * vedistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions 14 | * are met: 15 | * 1. vedistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * 2. vedistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 3. vedistributions in any form must be accompanied by information on 21 | * how to obtain complete source code for the Vedis engine and any 22 | * accompanying software that uses the Vedis engine software. 23 | * The source code must either be included in the distribution 24 | * or be available for no more than the cost of distribution plus 25 | * a nominal fee, and must be freely vedistributable under reasonable 26 | * conditions. For an executable file, complete source code means 27 | * the source code for all modules it contains.It does not include 28 | * source code for modules or files that typically accompany the major 29 | * components of the operating system on which the executable file runs. 30 | * 31 | * THIS SOFTWARE IS PROVIDED BY SYMISC SYSTEMS ``AS IS'' AND ANY EXPRESS 32 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 33 | * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 34 | * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SYMISC SYSTEMS 35 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 38 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 39 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 40 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 41 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 | */ 43 | 44 | #include 45 | #include "vedis.h" 46 | #include "mruby.h" 47 | #include "mruby/data.h" 48 | #include "mruby/variable.h" 49 | #include "mruby/array.h" 50 | #include "mruby/hash.h" 51 | #include "mruby/string.h" 52 | #include "mruby/class.h" 53 | #include "mrb_vedis.h" 54 | 55 | #define DONE mrb_gc_arena_restore(mrb, 0); 56 | 57 | static void mrb_vedis_error(mrb_state *mrb, vedis *store, const char *msg) 58 | { 59 | const char *err; 60 | int elen = 0; 61 | 62 | if (store) { 63 | vedis_config(store, VEDIS_CONFIG_ERR_LOG, &err, &elen); 64 | if (elen > 0) { 65 | vedis_lib_shutdown(); 66 | mrb_raisef(mrb, E_RUNTIME_ERROR, "vedis error: %S", mrb_str_new_cstr(mrb, err)); 67 | } 68 | } else { 69 | if (msg) { 70 | vedis_lib_shutdown(); 71 | mrb_raisef(mrb, E_RUNTIME_ERROR, "vedis error: %S", mrb_str_new_cstr(mrb, msg)); 72 | } 73 | } 74 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis unexpected error"); 75 | } 76 | 77 | static void mrb_vedis_ctx_free(mrb_state *mrb, void *p) 78 | { 79 | if (!p) { return; } 80 | vedis_close(p); 81 | } 82 | 83 | static const struct mrb_data_type vedis_ctx_type = { 84 | "vedis_ctx", mrb_vedis_ctx_free, 85 | }; 86 | 87 | static mrb_value mrb_vedis_open(mrb_state *mrb, mrb_value self) 88 | { 89 | int ret, argc; 90 | vedis *vstore; 91 | mrb_value db_file; 92 | 93 | vstore = (vedis *)DATA_PTR(self); 94 | if (vstore) { 95 | mrb_free(mrb, vstore); 96 | } 97 | DATA_TYPE(self) = &vedis_ctx_type; 98 | DATA_PTR(self) = NULL; 99 | 100 | argc = mrb_get_args(mrb, "|o", &db_file); 101 | ret = vedis_open(&vstore, argc == 0 ? ":mem:" : RSTRING_PTR(db_file)); 102 | if (ret != VEDIS_OK) { 103 | mrb_vedis_error(mrb, 0, "Out of memory"); 104 | } 105 | DATA_PTR(self) = vstore; 106 | 107 | return self; 108 | } 109 | 110 | static mrb_value mrb_vedis_set(mrb_state *mrb, mrb_value self) 111 | { 112 | int ret; 113 | vedis *vstore = DATA_PTR(self); 114 | mrb_value key_obj, val_obj; 115 | const char *key = NULL; 116 | 117 | mrb_get_args(mrb, "oo", &key_obj, &val_obj); 118 | switch (mrb_type(key_obj)) { 119 | case MRB_TT_STRING: 120 | key = RSTRING_PTR(key_obj); 121 | break; 122 | case MRB_TT_SYMBOL: 123 | key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); 124 | break; 125 | default: 126 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); 127 | } 128 | val_obj = mrb_obj_as_string(mrb, val_obj); 129 | ret = vedis_kv_store(vstore, key, strlen(key), RSTRING_PTR(val_obj), RSTRING_LEN(val_obj)); 130 | if (ret != VEDIS_OK) { 131 | mrb_vedis_error(mrb, vstore, 0); 132 | } 133 | 134 | return val_obj; 135 | } 136 | 137 | static mrb_value mrb_vedis_get(mrb_state *mrb, mrb_value self) 138 | { 139 | int ret; 140 | vedis *vstore = DATA_PTR(self); 141 | mrb_value key_obj; 142 | vedis_value *result; 143 | const char *key = NULL; 144 | 145 | mrb_get_args(mrb, "o", &key_obj); 146 | switch (mrb_type(key_obj)) { 147 | case MRB_TT_STRING: 148 | key = mrb_str_to_cstr(mrb, key_obj); 149 | break; 150 | case MRB_TT_SYMBOL: 151 | key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); 152 | break; 153 | default: 154 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); 155 | } 156 | ret = vedis_exec_fmt(vstore, "GET %s", key); 157 | if (ret != VEDIS_OK) { 158 | return mrb_nil_value(); 159 | } 160 | ret = vedis_exec_result(vstore, &result); 161 | if (ret != VEDIS_OK) { 162 | mrb_vedis_error(mrb, vstore, 0); 163 | } else { 164 | const char *val = vedis_value_to_string(result, 0); 165 | return mrb_str_new_cstr(mrb, val); 166 | } 167 | return mrb_nil_value(); 168 | } 169 | 170 | static mrb_value mrb_vedis_exec(mrb_state *mrb, mrb_value self) 171 | { 172 | int ret; 173 | vedis *vstore = DATA_PTR(self); 174 | vedis_value *result; 175 | vedis_value *entry; 176 | mrb_value ary; 177 | const char *cmd = NULL; 178 | 179 | mrb_get_args(mrb, "z", &cmd); 180 | ret = vedis_exec(vstore, cmd, -1); 181 | if (ret != VEDIS_OK) { 182 | return mrb_nil_value(); 183 | } 184 | ret = vedis_exec_result(vstore, &result); 185 | if (ret != VEDIS_OK) { 186 | return mrb_nil_value(); 187 | } else { 188 | if (vedis_value_is_string(result)) { 189 | return mrb_str_new_cstr(mrb, vedis_value_to_string(result, 0)); 190 | } else if (vedis_value_is_array(result)) { 191 | ary = mrb_ary_new(mrb); 192 | while ((entry = vedis_array_next_elem(result)) != 0) { 193 | mrb_ary_push(mrb, ary, mrb_str_new_cstr(mrb, vedis_value_to_string(entry, 0))); 194 | } 195 | return ary; 196 | } 197 | } 198 | return mrb_nil_value(); 199 | } 200 | 201 | static mrb_value mrb_vedis_del(mrb_state *mrb, mrb_value self) 202 | { 203 | int ret; 204 | vedis *vstore = DATA_PTR(self); 205 | mrb_value key; 206 | 207 | mrb_get_args(mrb, "o", &key); 208 | ret = vedis_kv_delete(vstore, RSTRING_PTR(key), -1); 209 | if (ret != VEDIS_OK) { 210 | mrb_vedis_error(mrb, vstore, 0); 211 | } 212 | 213 | return key; 214 | } 215 | 216 | static mrb_value mrb_vedis_append_s(mrb_state *mrb, mrb_value key_obj, mrb_value val_obj, vedis *vstore) 217 | { 218 | int ret; 219 | const char *key = NULL; 220 | size_t key_len; 221 | 222 | switch (mrb_type(key_obj)) { 223 | case MRB_TT_STRING: 224 | key = RSTRING_PTR(key_obj); 225 | key_len = RSTRING_LEN(key_obj); 226 | break; 227 | case MRB_TT_SYMBOL: 228 | key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); 229 | key_len = strlen(key); 230 | break; 231 | default: 232 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); 233 | } 234 | val_obj = mrb_obj_as_string(mrb, val_obj); 235 | ret = vedis_kv_append(vstore, key, key_len, RSTRING_PTR(val_obj), RSTRING_LEN(val_obj)); 236 | if (ret != VEDIS_OK) { 237 | mrb_vedis_error(mrb, vstore, 0); 238 | } 239 | return mrb_true_value(); 240 | } 241 | 242 | static mrb_value mrb_vedis_append(mrb_state *mrb, mrb_value self) 243 | { 244 | vedis *vstore = DATA_PTR(self); 245 | mrb_value key_obj, val_obj; 246 | mrb_get_args(mrb, "oo", &key_obj, &val_obj); 247 | return mrb_vedis_append_s(mrb, key_obj, val_obj, vstore); 248 | } 249 | 250 | static mrb_value mrb_vedis_append_hash(mrb_state *mrb, mrb_value self) 251 | { 252 | int ai; 253 | vedis *vstore = DATA_PTR(self); 254 | mrb_value hash, keys, key, val; 255 | 256 | mrb_get_args(mrb, "H", &hash); 257 | mrb_gc_protect(mrb, hash); 258 | keys = mrb_hash_keys(mrb, hash); 259 | ai = mrb_gc_arena_save(mrb); 260 | while (!mrb_nil_p(key = mrb_ary_pop(mrb, keys))) { 261 | val = mrb_hash_get(mrb, hash, key); 262 | mrb_vedis_append_s(mrb, key, val, vstore); 263 | mrb_gc_arena_restore(mrb, ai); 264 | } 265 | return mrb_true_value(); 266 | } 267 | 268 | static mrb_value mrb_vedis_exists(mrb_state *mrb, mrb_value self) 269 | { 270 | int ret; 271 | vedis *vstore = DATA_PTR(self); 272 | mrb_value key_obj; 273 | vedis_value *result; 274 | const char *key = NULL; 275 | 276 | mrb_get_args(mrb, "o", &key_obj); 277 | switch (mrb_type(key_obj)) { 278 | case MRB_TT_STRING: 279 | key = RSTRING_PTR(key_obj); 280 | break; 281 | case MRB_TT_SYMBOL: 282 | key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); 283 | break; 284 | default: 285 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); 286 | } 287 | ret = vedis_exec_fmt(vstore, "EXISTS %s", key); 288 | if (ret != VEDIS_OK) { 289 | mrb_vedis_error(mrb, vstore, 0); 290 | } 291 | ret = vedis_exec_result(vstore, &result); 292 | if (ret != VEDIS_OK) { 293 | mrb_vedis_error(mrb, vstore, 0); 294 | } 295 | if (vedis_value_to_bool(result)) { 296 | return mrb_true_value(); 297 | } 298 | return mrb_false_value(); 299 | } 300 | 301 | static mrb_value mrb_vedis_strlen(mrb_state *mrb, mrb_value self) 302 | { 303 | int ret; 304 | vedis *vstore = DATA_PTR(self); 305 | mrb_value key_obj; 306 | vedis_value *result; 307 | const char *key = NULL; 308 | 309 | mrb_get_args(mrb, "o", &key_obj); 310 | switch (mrb_type(key_obj)) { 311 | case MRB_TT_STRING: 312 | key = RSTRING_PTR(key_obj); 313 | break; 314 | case MRB_TT_SYMBOL: 315 | key = mrb_sym2name(mrb, mrb_obj_to_sym(mrb, key_obj)); 316 | break; 317 | default: 318 | mrb_raise(mrb, E_RUNTIME_ERROR, "vedis key type is string or symbol"); 319 | } 320 | ret = vedis_exec_fmt(vstore, "STRLEN %s", key); 321 | if (ret != VEDIS_OK) { 322 | return mrb_nil_value(); 323 | } 324 | ret = vedis_exec_result(vstore, &result); 325 | if (ret != VEDIS_OK) { 326 | mrb_vedis_error(mrb, vstore, 0); 327 | return mrb_nil_value(); 328 | } else { 329 | return mrb_fixnum_value(vedis_value_to_int(result)); 330 | } 331 | } 332 | 333 | static mrb_value mrb_vedis_close(mrb_state *mrb, mrb_value self) 334 | { 335 | int ret; 336 | vedis *vstore = DATA_PTR(self); 337 | 338 | if (!vstore) { return self; } 339 | 340 | ret = vedis_close(vstore); 341 | if (ret != VEDIS_OK) { 342 | mrb_vedis_error(mrb, vstore, 0); 343 | } 344 | 345 | DATA_PTR(self) = NULL; 346 | 347 | return self; 348 | } 349 | 350 | static mrb_value mrb_vedis_commit(mrb_state *mrb, mrb_value self) 351 | { 352 | int ret; 353 | vedis *vstore = DATA_PTR(self); 354 | 355 | ret = vedis_commit(vstore); 356 | if (ret != VEDIS_OK) { 357 | mrb_vedis_error(mrb, vstore, 0); 358 | } 359 | 360 | return mrb_true_value(); 361 | } 362 | 363 | void mrb_mruby_vedis_gem_init(mrb_state *mrb) 364 | { 365 | struct RClass *vedis; 366 | 367 | vedis = mrb_define_class(mrb, "Vedis", mrb->object_class); 368 | MRB_SET_INSTANCE_TT(vedis, MRB_TT_DATA); 369 | 370 | mrb_define_method(mrb, vedis, "initialize", mrb_vedis_open, MRB_ARGS_OPT(1)); 371 | mrb_define_method(mrb, vedis, "set", mrb_vedis_set, MRB_ARGS_REQ(2)); 372 | mrb_define_method(mrb, vedis, "get", mrb_vedis_get, MRB_ARGS_REQ(1)); 373 | mrb_define_method(mrb, vedis, "[]=", mrb_vedis_set, MRB_ARGS_REQ(2)); 374 | mrb_define_method(mrb, vedis, "[]", mrb_vedis_get, MRB_ARGS_REQ(1)); 375 | mrb_define_method(mrb, vedis, "exec", mrb_vedis_exec, MRB_ARGS_REQ(1)); 376 | mrb_define_method(mrb, vedis, "del", mrb_vedis_del, MRB_ARGS_REQ(1)); 377 | mrb_define_method(mrb, vedis, "append", mrb_vedis_append, MRB_ARGS_REQ(2)); 378 | mrb_define_method(mrb, vedis, "<<", mrb_vedis_append_hash, MRB_ARGS_REQ(1)); 379 | mrb_define_method(mrb, vedis, "exists?", mrb_vedis_exists, MRB_ARGS_REQ(1)); 380 | mrb_define_method(mrb, vedis, "strlen", mrb_vedis_strlen, MRB_ARGS_REQ(1)); 381 | mrb_define_method(mrb, vedis, "close", mrb_vedis_close, MRB_ARGS_NONE()); 382 | mrb_define_method(mrb, vedis, "commit", mrb_vedis_commit, MRB_ARGS_NONE()); 383 | DONE; 384 | } 385 | 386 | void mrb_mruby_vedis_gem_final(mrb_state *mrb) 387 | { 388 | } 389 | 390 | //#endif 391 | -------------------------------------------------------------------------------- /src/mrb_vedis.h: -------------------------------------------------------------------------------- 1 | /* 2 | // mrb_vedis.h - to provide vedis methods 3 | // 4 | // See Copyright Notice in mrb_vedis.c 5 | */ 6 | 7 | #ifndef MRB_VEDIS_H 8 | #define MRB_VEDIS_H 9 | 10 | void mrb_mruby_vedis_gem_init(mrb_state *mrb); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /test/vedis.rb: -------------------------------------------------------------------------------- 1 | ## 2 | ## Vedis Test 3 | ## 4 | 5 | assert("Vedis#set, Vedis#get") do 6 | v = Vedis.new 7 | v.set "hoge", "fuga" 8 | assert_equal "fuga", v.get("hoge") 9 | end 10 | 11 | assert("Vedis#set, Vedis#get by fixnum value") do 12 | v = Vedis.new 13 | v.set "hoge", 1 14 | assert_equal "1", v.get("hoge") 15 | end 16 | 17 | assert("Vedis#set, Vedis#get by sym") do 18 | v = Vedis.new 19 | v.set :hoge, "fuga" 20 | assert_equal "fuga", v.get(:hoge) 21 | end 22 | 23 | assert("Vedis#[]=, Vedis#[]") do 24 | v = Vedis.new 25 | v["hoge"] = "fuga" 26 | assert_equal "fuga", v["hoge"] 27 | end 28 | 29 | assert("Vedis#[]=, Vedis#[] by fixnum value") do 30 | v = Vedis.new 31 | v["hoge"] = 1 32 | assert_equal "1", v["hoge"] 33 | end 34 | 35 | assert("Vedis#[]=, Vedis#[] by sym") do 36 | v = Vedis.new 37 | v[:hoge] = "fuga" 38 | assert_equal "fuga", v[:hoge] 39 | end 40 | 41 | assert("Vedis#[] by On-Disk Data Store") do 42 | v = Vedis.new "/tmp/mruby-vedis-test.db" 43 | v[:hoge] = "fuga" 44 | assert_equal "fuga", v[:hoge] 45 | end 46 | 47 | assert("Vedis#exec") do 48 | v = Vedis.new 49 | r1 = v.exec "MSET username james age 27 mail dude@example.com" 50 | r2 = v.exec "MGET username age mail" 51 | assert_equal nil, r1 52 | assert_equal ["james", "27", "dude@example.com"] , r2 53 | end 54 | 55 | assert("Vedis#append") do 56 | v = Vedis.new 57 | v["hoge"] = "aaa" 58 | assert_equal(true, v.append("hoge", " bbb")) 59 | assert_equal("aaa bbb", v["hoge"]) 60 | assert_equal(true, v.append("fuga", "ccc")) 61 | assert_equal("ccc", v["fuga"]) 62 | end 63 | 64 | assert("Vedis#append, by sym") do 65 | v = Vedis.new 66 | v[:hoge] = "aaa" 67 | assert_equal(true, v.append(:hoge, " bbb")) 68 | assert_equal("aaa bbb", v[:hoge]) 69 | assert_equal(true, v.append(:fuga, "ccc")) 70 | assert_equal("ccc", v[:fuga]) 71 | end 72 | 73 | assert("Vedis#<<") do 74 | v = Vedis.new 75 | v["hoge"] = "aaa" 76 | assert_equal(true, v << {:fuga => "ccc", :hoge => " bbb"}) 77 | assert_equal("aaa bbb", v["hoge"]) 78 | assert_equal("ccc", v["fuga"]) 79 | end 80 | 81 | assert("Vedis#exist?") do 82 | v = Vedis.new 83 | v["hoge"] = "aaa" 84 | assert_true(v.exists?("hoge")) 85 | assert_false(v.exists?("fuga")) 86 | end 87 | 88 | assert("Vedis#exist?, by sym") do 89 | v = Vedis.new 90 | v[:hoge] = "aaa" 91 | assert_true(v.exists?(:hoge)) 92 | assert_false(v.exists?(:fuga)) 93 | end 94 | 95 | assert("Vedis#strlen") do 96 | v = Vedis.new 97 | v["hoge"] = "aaa" 98 | assert_equal(3, v.strlen("hoge")) 99 | assert_equal(0, v.strlen("fuga")) 100 | end 101 | 102 | assert("Vedis#strlen, by sym") do 103 | v = Vedis.new 104 | v[:hoge] = "aaa" 105 | assert_equal(3, v.strlen(:hoge)) 106 | assert_equal(0, v.strlen(:fuga)) 107 | end 108 | 109 | assert("Vedis#close") do 110 | v = Vedis.new 111 | v.close 112 | v.close # ignore second close 113 | end 114 | --------------------------------------------------------------------------------