├── .clang-format ├── .gitignore ├── .travis.yml ├── README.md ├── Rakefile ├── build_config.rb ├── example └── redis.rb ├── include └── mruby │ └── redis.h ├── mrbgem.rake ├── mrblib └── error.rb ├── src ├── Makefile ├── mrb_redis.c └── mrb_redis.h └── test └── redis.rb /.clang-format: -------------------------------------------------------------------------------- 1 | # requires clang-format >= 3.6 2 | BasedOnStyle: "LLVM" 3 | IndentWidth: 2 4 | ColumnLimit: 120 5 | BreakBeforeBraces: Linux 6 | AllowShortFunctionsOnASingleLine: None 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /mruby/ 2 | src/tags 3 | src/TAGS 4 | tags 5 | TAGS 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | addons: 3 | apt: 4 | packages: 5 | - rake 6 | - bison 7 | - git 8 | - gperf 9 | - redis-server 10 | language: c 11 | compiler: 12 | - gcc 13 | - clang 14 | before_script: 15 | - redis-server --port 6379 & 16 | - redis-server --port 6380 --requirepass 'secret' & 17 | - redis-server --cluster-enabled yes --cluster-config-file 7000-nodes.conf --port 7000 & 18 | - redis-server --cluster-enabled yes --cluster-config-file 7001-nodes.conf --port 7001 & 19 | - redis-server --cluster-enabled yes --cluster-config-file 7002-nodes.conf --port 7002 & 20 | - redis-server --cluster-enabled yes --cluster-config-file 7003-nodes.conf --port 7003 & 21 | - redis-server --cluster-enabled yes --cluster-config-file 7004-nodes.conf --port 7004 & 22 | - redis-server --cluster-enabled yes --cluster-config-file 7005-nodes.conf --port 7005 & 23 | - echo yes | redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 24 | script: 25 | - rake test 26 | notifications: 27 | irc: 28 | channels: 29 | - "irc.freenode.org#backdoor-log" 30 | on_success: always 31 | on_failure: always 32 | use_notice: true 33 | skip_join: true 34 | template: 35 | - "[%{message}] %{repository} (%{commit}) by %{author}: See %{build_url}" 36 | webhooks: 37 | - secure: "B40Dmvt0JkAQA/dfhUP+NYFI37pBkxRBrc+XudT2bCilj3Z99B1l36oHxfy9Ate01yDPNrq7pCT2WGmLhfumXwwiucxcSpCg06D563IPZsrc4fnhqwTyWzTu8YUvEYgUONG5gokPOAIP9yGXNbyel9+wPVZitOkVTPRrm3PNbiE=" 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mruby-redis [![Build Status](https://travis-ci.org/matsumotory/mruby-redis.svg?branch=master)](https://travis-ci.org/matsumotory/mruby-redis) 2 | 3 | [Hiredis](https://github.com/redis/hiredis) binding for mruby. Hiredis is a 4 | minimalistic C client library for the Redis database. Redis is an open source, 5 | BSD-licensed, advanced key-value store. It is often referred to as a data 6 | structure server since keys can contain strings, hashes, lists, sets and sorted 7 | sets. Plese visit [redis' official website](http://redis.io/) for more details 8 | about Redis. 9 | 10 | __Running Redis might be impossible for memory/CPU-constrained environments,__ 11 | so we can recommend [mruby-vedis](https://github.com/matsumoto-r/mruby-vedis). 12 | `vedis` is an embeddable datastore distributed as a C library. It supports over 13 | 70 commands similar to Redis, but runs in memory (hence doesn't require a 14 | networking layer). 15 | Please visit [vedis' website](http://vedis.symisc.net/index.html) for more 16 | details. 17 | 18 | ## INSTALLATION 19 | 20 | #### Using mrbgems 21 | 22 | Add conf.gem line to `build_config.rb`: 23 | 24 | ```ruby 25 | MRuby::Build.new do |conf| 26 | 27 | # ... (snip) ... 28 | 29 | conf.gem :git => 'https://github.com/matsumoto-r/mruby-redis.git' 30 | end 31 | ``` 32 | 33 | 34 | ## USAGE 35 | 36 | ### Connecting to a Redis server 37 | 38 | ```ruby 39 | client = Redis.new "127.0.0.1", 6379, 2 # Connect to the server 40 | client.host # => "127.0.0.1" 41 | client.port # => 6379 42 | client.select 0 # Select the database 43 | client.auth "secret" # Required if Redis is password-protected 44 | client.enable_keepalive # Turn on TCP keepalive if needed 45 | client.keepalive # => :on 46 | ``` 47 | 48 | ### Commands 49 | 50 | #### `Redis#auth` [doc](http://redis.io/commands/auth) 51 | 52 | ```ruby 53 | client.auth "secret" 54 | ``` 55 | 56 | #### `Redis#asking` 57 | 58 | ```ruby 59 | client.asking 60 | ``` 61 | 62 | 63 | #### `Redis#[]=` 64 | 65 | TBD 66 | 67 | 68 | #### `Redis#[]` 69 | 70 | ```ruby 71 | client["key"] 72 | ``` 73 | 74 | #### `Redis#bulk_reply` 75 | 76 | TBD 77 | 78 | 79 | #### `Redis#close` 80 | 81 | TBD 82 | 83 | 84 | #### `Redis#cluster` [doc](http://redis.io/commands/cluster-info) 85 | 86 | ```ruby 87 | client.cluster "info" 88 | client.cluster "nodes" 89 | client.cluster "slots" 90 | ``` 91 | 92 | 93 | #### `Redis#decr` [doc](http://redis.io/commands/decr) 94 | 95 | ```ruby 96 | client.decr "key" 97 | ``` 98 | 99 | 100 | #### `Redis#decrby` [doc](http://redis.io/commands/decrby) 101 | 102 | TBD 103 | 104 | 105 | #### `Redis#del` [doc](http://redis.io/commands/del) 106 | 107 | ```ruby 108 | client.del "key" 109 | ``` 110 | 111 | #### `Redis#discard` [doc](http://redis.io/commands/discard) 112 | 113 | ```ruby 114 | # discard the transaction 115 | client.discard 116 | ``` 117 | 118 | #### `Redis#exec` [doc](http://redis.io/commands/exec) 119 | 120 | ```ruby 121 | # execute the transaction 122 | client.exec 123 | ``` 124 | 125 | #### `Redis#exists?` [doc](http://redis.io/commands/exists?) 126 | 127 | ```ruby 128 | client.exists?("key") 129 | ``` 130 | 131 | 132 | #### `Redis#expire` [doc](http://redis.io/commands/expire) 133 | 134 | TBD 135 | 136 | 137 | #### `Redis#flushall` [doc](http://redis.io/commands/flushall) 138 | 139 | ```ruby 140 | client.flushall 141 | ``` 142 | 143 | 144 | #### `Redis#flushdb` [doc](http://redis.io/commands/flushdb) 145 | 146 | ```ruby 147 | client.flushdb 148 | ``` 149 | 150 | 151 | #### `Redis#get` [doc](http://redis.io/commands/get) 152 | 153 | ```ruby 154 | client.get "key" 155 | ``` 156 | 157 | 158 | #### `Redis#hdel` [doc](http://redis.io/commands/hdel) 159 | 160 | ```ruby 161 | client.hdel "myhash", "field1" 162 | ``` 163 | 164 | 165 | #### `Redis#hexists?` [doc](http://redis.io/commands/hexists) 166 | 167 | ```ruby 168 | client.hexists? "myhash", "field1" 169 | ``` 170 | 171 | 172 | #### `Redis#hget` [doc](http://redis.io/commands/hget) 173 | 174 | ```ruby 175 | client.hget "myhash", "field1" 176 | ``` 177 | 178 | 179 | #### `Redis#hgetall` [doc](http://redis.io/commands/hgetall) 180 | 181 | TBD 182 | 183 | 184 | #### `Redis#hkeys` [doc](http://redis.io/commands/hkeys) 185 | 186 | TBD 187 | 188 | #### `Redis#hmset` [doc](http://redis.io/commands/hmset) 189 | 190 | ```ruby 191 | client.hmset "myhash", "field1", "a", "field2", "b" 192 | ``` 193 | 194 | 195 | #### `Redis#hmget` [doc](http://redis.io/commands/hmget) 196 | 197 | ```ruby 198 | client.hmget "myhash", "field1", "field2" 199 | ``` 200 | 201 | 202 | #### `Redis#hvals` [doc](http://redis.io/commands/hvals) 203 | 204 | ```ruby 205 | client.hvals "myhash" 206 | ``` 207 | 208 | 209 | #### `Redis#hset` [doc](http://redis.io/commands/hset) 210 | 211 | ```ruby 212 | client.hset "myhash", "field1", "a" 213 | ``` 214 | 215 | 216 | #### `Redis#hsetnx` [doc](http://redis.io/commands/hsetnx) 217 | 218 | ```ruby 219 | client.hsetnx "myhash", "field1", "a" 220 | ``` 221 | 222 | 223 | #### `Redis#hincrby` [doc](http://redis.io/commands/hincrby) 224 | 225 | ```ruby 226 | client.hincrby "myhash", "field", 1 227 | ``` 228 | 229 | 230 | #### `Redis#incr` [doc](http://redis.io/commands/incr) 231 | 232 | ```ruby 233 | client.incr "key" 234 | ``` 235 | 236 | 237 | #### `Redis#incrby` [doc](http://redis.io/commands/incrby) 238 | 239 | TBD 240 | 241 | 242 | #### `Redis#keys` [doc](http://redis.io/commands/keys) 243 | 244 | TBD 245 | 246 | 247 | #### `Redis#lindex` [doc](http://redis.io/commands/lindex) 248 | 249 | TBD 250 | 251 | 252 | #### `Redis#llen` [doc](http://redis.io/commands/llen) 253 | 254 | TBD 255 | 256 | 257 | #### `Redis#lpop` [doc](http://redis.io/commands/lpop) 258 | 259 | TBD 260 | 261 | 262 | #### `Redis#lpush` [doc](http://redis.io/commands/lpush) 263 | 264 | TBD 265 | 266 | 267 | #### `Redis#lrange` [doc](http://redis.io/commands/lrange) 268 | 269 | ```ruby 270 | client.lrange "logs", 0, -1 271 | ``` 272 | 273 | 274 | #### `Redis#ltrim` [doc](http://redis.io/commands/ltrim) 275 | 276 | ```ruby 277 | client.ltrim "logs", 1, -1 278 | ``` 279 | 280 | #### `Redis#mget` [doc](http://redis.io/commnads/mget) 281 | ```ruby 282 | client.mget "key1", "key2" 283 | ``` 284 | 285 | #### `Redis#mset` [doc](http://redis.io/commnads/mset) 286 | 287 | ```ruby 288 | client.mset "key1", "value1", "key2", "value2" 289 | ``` 290 | 291 | #### `Redis#multi` [doc](http://redis.io/commands/multi) 292 | 293 | ```ruby 294 | # start new transaction. Its finished by exec or discard 295 | client.multi 296 | ``` 297 | 298 | 299 | #### `Redis#ping` [doc](http://redis.io/commands/ping) 300 | 301 | ```ruby 302 | pong = client.ping 303 | ``` 304 | 305 | 306 | #### `Redis#publish` [doc](http://redis.io/commands/publish) 307 | 308 | ```ruby 309 | number_of_subscribed_clients = client.publish "queue", "some value" 310 | ``` 311 | 312 | 313 | #### `Redis#pfadd` [doc](http://redis.io/commands/pfadd) 314 | 315 | ```ruby 316 | number_of_internal_registers_altered = client.pfadd "hyperloglog_structure", "some value" 317 | ``` 318 | 319 | #### `Redis#pfcount` [doc](http://redis.io/commands/pfcount) 320 | 321 | ```ruby 322 | approximated_number_of_unique_elements = client.pfcount "hyperloglog_structure" 323 | ``` 324 | 325 | #### `Redis#pfmerge` [doc](http://redis.io/commands/pfmerge) 326 | 327 | ```ruby 328 | client.pfmerge "hll3", "hll1", "hll2" 329 | ``` 330 | 331 | 332 | #### `Redis#queue` [doc](http://redis.io/commands/queue) 333 | 334 | TBD 335 | 336 | 337 | #### `Redis#randomkey` [doc](http://redis.io/commands/randomkey) 338 | 339 | TBD 340 | 341 | 342 | #### `Redis#reply` [doc](http://redis.io/commands/reply) 343 | 344 | TBD 345 | 346 | 347 | #### `Redis#rpop` [doc](http://redis.io/commands/rpop) 348 | 349 | TBD 350 | 351 | 352 | #### `Redis#rpush` [doc](http://redis.io/commands/rpush) 353 | 354 | TBD 355 | 356 | 357 | #### `Redis#sadd` [doc](http://redis.io/commands/sadd) 358 | 359 | TBD 360 | 361 | 362 | #### `Redis#scard` [doc](http://redis.io/commands/scard) 363 | 364 | TBD 365 | 366 | 367 | #### `Redis#set` [doc](http://redis.io/commands/set) 368 | 369 | ```ruby 370 | client.set key, "200" 371 | ``` 372 | 373 | 374 | #### `Redis#setnx` [doc](http://redis.io/commands/setnx) 375 | 376 | ```ruby 377 | client.setnx key, "foo" # => true 378 | client.setnx key, "bar" # => false 379 | client.get key # => "foo" 380 | ``` 381 | 382 | 383 | #### `Redis#sismember` [doc](http://redis.io/commands/sismember) 384 | 385 | TBD 386 | 387 | 388 | #### `Redis#smembers` [doc](http://redis.io/commands/smembers) 389 | 390 | TBD 391 | 392 | 393 | #### `Redis#spop` [doc](http://redis.io/commands/spop) 394 | 395 | TBD 396 | 397 | #### `Redis#srem` [doc](http://redis.io/commands/srem) 398 | 399 | ```ruby 400 | client.srem "set", "fuge", "hoga" 401 | ``` 402 | 403 | 404 | #### `Redis#ttl` [doc](http://redis.io/commands/ttl) 405 | 406 | TBD 407 | 408 | 409 | #### `Redis#unwatch` [doc](http://redis.io/commands/unwatch) 410 | 411 | ```ruby 412 | client.unwatch 413 | ``` 414 | 415 | 416 | #### `Redis#watch` [doc](http://redis.io/commands/watch) 417 | 418 | ```ruby 419 | # watch key(s) for a transaction 420 | client.watch "key1", "key2" 421 | ``` 422 | 423 | 424 | #### `Redis#zadd` [doc](http://redis.io/commands/zadd) 425 | 426 | ```ruby 427 | client.zadd "hs", 80, "a" 428 | ``` 429 | 430 | 431 | #### `Redis#zcard` [doc](http://redis.io/commands/zcard) 432 | 433 | TBD 434 | 435 | 436 | #### `Redis#zrange` [doc](http://redis.io/commands/zrange) 437 | 438 | ```ruby 439 | client.zrange "hs", 0, -1 440 | ``` 441 | 442 | 443 | #### `Redis#zrank` [doc](http://redis.io/commands/zrank) 444 | 445 | ```ruby 446 | client.zrank "hs", "a" 447 | ``` 448 | 449 | 450 | #### `Redis#zrevrange` [doc](http://redis.io/commands/zrevrange) 451 | 452 | ```ruby 453 | client.zrevrange "hs", 0, -1 454 | ``` 455 | 456 | 457 | #### `Redis#zrevrank` [doc](http://redis.io/commands/zrevrank) 458 | 459 | ```ruby 460 | client.zrevrank "hs", "a" 461 | ``` 462 | 463 | 464 | #### `Redis#zscore` [doc](http://redis.io/commands/zscore) 465 | 466 | ```ruby 467 | client.zscore "hs", "a" 468 | ``` 469 | 470 | See [`example/redis.rb`](https://github.com/matsumoto-r/mruby-redis/blob/master/example/redis.rb) for more details. 471 | 472 | ## LICENSE 473 | 474 | MIT License - Copyright (c) mod\_mruby developers 2012 475 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || "build_config.rb") 2 | MRUBY_VERSION=ENV["MRUBY_VERSION"] || "1.1.0" 3 | 4 | file :mruby do 5 | #sh "wget -O mruby.tar.gz https://github.com/mruby/mruby/archive/#{MRUBY_VERSION}.tar.gz" 6 | #sh "tar -xvzf mruby.tar.gz" 7 | #sh "rm mruby.tar.gz" 8 | #sh "mv mruby-#{MRUBY_VERSION} mruby" 9 | sh "git clone --depth=1 git://github.com/mruby/mruby.git" 10 | end 11 | 12 | desc "compile binary" 13 | task :compile => :mruby do 14 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all" 15 | end 16 | 17 | desc "test" 18 | task :test => :mruby do 19 | sh "cd mruby && MRUBY_CONFIG=#{MRUBY_CONFIG} rake all test" 20 | end 21 | 22 | desc "cleanup" 23 | task :clean do 24 | sh "cd mruby && rake deep_clean" 25 | end 26 | 27 | task :default => :test 28 | -------------------------------------------------------------------------------- /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 | conf.gem :github => 'matsumoto-r/mruby-sleep' 18 | conf.enable_test 19 | 20 | # C compiler settings 21 | # conf.cc do |cc| 22 | # cc.command = ENV['CC'] || 'gcc' 23 | # cc.flags = [ENV['CFLAGS'] || %w()] 24 | # cc.include_paths = ["#{root}/include"] 25 | # cc.defines = %w(DISABLE_GEMS) 26 | # cc.option_include_path = '-I%s' 27 | # cc.option_define = '-D%s' 28 | # cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}" 29 | # end 30 | 31 | # mrbc settings 32 | # conf.mrbc do |mrbc| 33 | # mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers 34 | # end 35 | 36 | # Linker settings 37 | # conf.linker do |linker| 38 | # linker.command = ENV['LD'] || 'gcc' 39 | # linker.flags = [ENV['LDFLAGS'] || []] 40 | # linker.flags_before_libraries = [] 41 | # linker.libraries = %w() 42 | # linker.flags_after_libraries = [] 43 | # linker.library_paths = [] 44 | # linker.option_library = '-l%s' 45 | # linker.option_library_path = '-L%s' 46 | # linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}" 47 | # end 48 | 49 | # Archiver settings 50 | # conf.archiver do |archiver| 51 | # archiver.command = ENV['AR'] || 'ar' 52 | # archiver.archive_options = 'rs %{outfile} %{objs}' 53 | # end 54 | 55 | # Parser generator settings 56 | # conf.yacc do |yacc| 57 | # yacc.command = ENV['YACC'] || 'bison' 58 | # yacc.compile_options = '-o %{outfile} %{infile}' 59 | # end 60 | 61 | # gperf settings 62 | # conf.gperf do |gperf| 63 | # gperf.command = 'gperf' 64 | # gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}' 65 | # end 66 | 67 | # file extensions 68 | # conf.exts do |exts| 69 | # exts.object = '.o' 70 | # exts.executable = '' # '.exe' if Windows 71 | # exts.library = '.a' 72 | # end 73 | 74 | # file separetor 75 | # conf.file_separator = '/' 76 | end 77 | 78 | # Define cross build settings 79 | # MRuby::CrossBuild.new('32bit') do |conf| 80 | # toolchain :gcc 81 | # 82 | # conf.cc.flags << "-m32" 83 | # conf.linker.flags << "-m32" 84 | # 85 | # conf.build_mrbtest_lib_only 86 | # 87 | # conf.gem 'examples/mrbgems/c_and_ruby_extension_example' 88 | # 89 | # conf.test_runner.command = 'env' 90 | # 91 | # end 92 | -------------------------------------------------------------------------------- /example/redis.rb: -------------------------------------------------------------------------------- 1 | host = "127.0.0.1" 2 | port = 6379 3 | key = "hoge" 4 | database = 0 5 | 6 | puts "> redis connect #{host}: #{port.to_s}" 7 | r = Redis.new host, port 8 | 9 | puts "> redis select: #{database}" 10 | r.select database 11 | 12 | puts "> redis set #{key} 200" 13 | r.set key, "200" 14 | 15 | puts "> redis get #{key}" 16 | puts "#{key}: #{r[key]}" 17 | 18 | puts "> redis exists #{key}" 19 | puts "#{r.exists?(key)}" 20 | 21 | puts "> redis exists fuga" 22 | puts "#{r.exists?("fuga")}" 23 | 24 | puts "> redis set #{key} fuga" 25 | r[key] = "fuga" 26 | 27 | puts "> redis get #{key}" 28 | puts "#{key}: #{r.get key}" 29 | 30 | puts "> redis del #{key}" 31 | r.del key 32 | 33 | if r[key].nil? 34 | puts "del success!" 35 | end 36 | 37 | puts "> redis incr #{key}" 38 | puts "#{key} incr: #{r.incr(key)}" 39 | puts "#{key} incr: #{r.incr(key)}" 40 | puts "#{key} incr: #{r.incr(key)}" 41 | puts "#{key} incr: #{r.incr(key)}" 42 | 43 | puts "> redis decr #{key}" 44 | puts "#{key} decr: #{r.decr(key)}" 45 | puts "#{key} decr: #{r.decr(key)}" 46 | puts "#{key} decr: #{r.decr(key)}" 47 | puts "#{key} decr: #{r.decr(key)}" 48 | 49 | puts "> redis lpush logs error" 50 | r.lpush "logs", "error1" 51 | r.lpush "logs", "error2" 52 | r.lpush "logs", "error3" 53 | 54 | puts "> redis lrange 0 -1" 55 | puts r.lrange "logs", 0, -1 56 | 57 | puts "> redis ltrim 1 -1" 58 | r.ltrim "logs", 1, -1 59 | 60 | puts "> redis lrange 0 -1" 61 | puts r.lrange "logs", 0, -1 62 | 63 | puts "> redis del logs" 64 | r.del "logs" 65 | 66 | if r["logs"].nil? 67 | puts "del success!" 68 | end 69 | 70 | puts "> redis hset myhash field1 a" 71 | r.hset "myhash", "field1", "a" 72 | 73 | puts "> redis hset myhash field2 b" 74 | r.hset "myhash", "field2", "b" 75 | 76 | puts "> redis hget myhash field1" 77 | puts r.hget "myhash", "field1" 78 | 79 | puts "> redis hget myhash field2" 80 | puts r.hget "myhash", "field2" 81 | 82 | puts "> redis hdel myhash field1" 83 | puts r.hdel "myhash", "field1" 84 | 85 | puts "> redis del myhash" 86 | 87 | if r["myhash"].nil? 88 | puts "del success!" 89 | end 90 | 91 | puts "> redis zadd hs 80 a" 92 | r.zadd "hs", 80, "a" 93 | 94 | puts "> redis zadd hs 50.1 b" 95 | r.zadd "hs", 50.1, "b" 96 | 97 | puts "> redis zadd hs 60 c" 98 | r.zadd "hs", 60, "c" 99 | 100 | puts "> redis zscore hs a" 101 | puts r.zscore "hs", "a" 102 | 103 | puts "> redis zrange hs 0 -1" 104 | puts r.zrange "hs", 0, -1 105 | 106 | puts "> redis zrank hs b" 107 | puts r.zrank "hs", "b" 108 | 109 | puts "> redis zrank hs c" 110 | puts r.zrank "hs", "c" 111 | 112 | puts "> redis zrank hs a" 113 | puts r.zrank "hs", "a" 114 | 115 | puts ">redis zrevrange hs 0 -1" 116 | puts r.zrevrange "hs", 0, -1 117 | 118 | puts ">redis zrevrank hs a" 119 | puts r.zrevrank "hs", "a" 120 | 121 | puts ">redis zrevrank hs c" 122 | puts r.zrevrank "hs", "c" 123 | 124 | puts ">redis zrevrank hs b" 125 | puts r.zrevrank "hs", "b" 126 | 127 | puts "> redis del hs" 128 | r.del "hs" 129 | if r["hs"].nil? 130 | puts "del success!" 131 | end 132 | 133 | puts "> redis publish :one hello" 134 | r.publish "one", "hello" 135 | 136 | r.close 137 | -------------------------------------------------------------------------------- /include/mruby/redis.h: -------------------------------------------------------------------------------- 1 | #ifndef MRUBY_REDIS_H 2 | #define MRUBY_REDIS_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define E_REDIS_ERROR (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "ConnectionError")) 11 | #define E_REDIS_REPLY_ERROR (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "ReplyError")) 12 | #ifndef E_EOF_ERROR 13 | #define E_EOF_ERROR (mrb_class_get(mrb, "EOFError")) 14 | #endif 15 | #define E_REDIS_ERR_PROTOCOL (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "ProtocolError")) 16 | #define E_REDIS_ERR_OOM (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "OOMError")) 17 | #define E_REDIS_ERR_AUTH (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "AuthError")) 18 | #define E_REDIS_ERR_CLOSED (mrb_class_get_under(mrb, mrb_class_get(mrb, "Redis"), "ClosedError")) 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif 25 | 26 | -------------------------------------------------------------------------------- /mrbgem.rake: -------------------------------------------------------------------------------- 1 | MRuby::Gem::Specification.new('mruby-redis') do |spec| 2 | spec.license = 'MIT' 3 | spec.authors = 'MATSUMOTO Ryosuke' 4 | spec.version = '0.0.1' 5 | # for expire test 6 | require 'open3' 7 | 8 | hiredis_dir = "#{build_dir}/hiredis" 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? hiredis_dir 22 | Dir.chdir(build_dir) do 23 | e = {} 24 | run_command e, 'git clone https://github.com/redis/hiredis.git' 25 | # Latest HIREDIS is not compatible for OS X 26 | run_command e, "git --git-dir=#{hiredis_dir}/.git --work-tree=#{hiredis_dir} checkout v0.13.3" if `uname` =~ /Darwin/ 27 | end 28 | end 29 | 30 | if ! File.exist? "#{hiredis_dir}/libhiredis.a" 31 | Dir.chdir hiredis_dir do 32 | e = { 33 | 'CC' => "#{spec.build.cc.command} #{spec.build.cc.flags.reject {|flag| flag == '-fPIE'}.join(' ')}", 34 | 'CXX' => "#{spec.build.cxx.command} #{spec.build.cxx.flags.join(' ')}", 35 | 'LD' => "#{spec.build.linker.command} #{spec.build.linker.flags.join(' ')}", 36 | 'AR' => spec.build.archiver.command, 37 | 'PREFIX' => hiredis_dir 38 | } 39 | make_command = `uname` =~ /BSD/ ? "gmake" : "make" 40 | run_command e, "#{make_command}" 41 | run_command e, "#{make_command} install" 42 | end 43 | end 44 | 45 | spec.cc.include_paths << "#{hiredis_dir}/include" 46 | spec.linker.flags_before_libraries << "#{hiredis_dir}/lib/libhiredis.a" 47 | 48 | spec.add_dependency "mruby-sleep" 49 | spec.add_dependency "mruby-pointer", :github => 'matsumotory/mruby-pointer' 50 | end 51 | -------------------------------------------------------------------------------- /mrblib/error.rb: -------------------------------------------------------------------------------- 1 | unless Object.const_defined?("IOError") 2 | class IOError < StandardError; end 3 | end 4 | 5 | unless Object.const_defined?("EOFError") 6 | class EOFError < IOError; end 7 | end 8 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | MRUBY_ROOT = tmp/mruby 2 | 3 | INCLUDES = -I$(MRUBY_ROOT)/include -I$(MRUBY_ROOT)/src -I. 4 | CFLAGS = $(INCLUDES) -O3 -g -Wall -Werror-implicit-function-declaration 5 | 6 | CC = gcc 7 | LL = gcc 8 | AR = ar 9 | 10 | all : libmruby.a libmrb_redis.a 11 | @echo done 12 | 13 | mrb_redis.o : mrb_redis.c mrb_redis.h 14 | gcc -c $(CFLAGS) mrb_redis.c 15 | 16 | libmrb_redis.a : mrb_redis.o 17 | $(AR) r libmrb_redis.a mrb_redis.o 18 | 19 | tmp/mruby: 20 | mkdir -p tmp 21 | cd tmp; git clone git://github.com/mruby/mruby.git 22 | 23 | libmruby.a: tmp/mruby 24 | cd tmp/mruby && make CFLAGS="-O3 -fPIC" 25 | 26 | clean : 27 | rm -f *.o libmrb_redis.a 28 | 29 | clobber: clean 30 | -rm -rf tmp 31 | -------------------------------------------------------------------------------- /src/mrb_redis.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** mrb_redis - redis class for mruby 3 | ** 4 | ** Copyright (c) mod_mruby developers 2012- 5 | ** 6 | ** Permission is hereby granted, free of charge, to any person obtaining 7 | ** a copy of this software and associated documentation files (the 8 | ** "Software"), to deal in the Software without restriction, including 9 | ** without limitation the rights to use, copy, modify, merge, publish, 10 | ** distribute, sublicense, and/or sell copies of the Software, and to 11 | ** permit persons to whom the Software is furnished to do so, subject to 12 | ** the following conditions: 13 | ** 14 | ** The above copyright notice and this permission notice shall be 15 | ** included in all copies or substantial portions of the Software. 16 | ** 17 | ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | ** 25 | ** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 26 | */ 27 | 28 | #include "mrb_redis.h" 29 | #include "mruby.h" 30 | #include "mruby/array.h" 31 | #include "mruby/class.h" 32 | #include "mruby/data.h" 33 | #include "mruby/hash.h" 34 | #include "mruby/numeric.h" 35 | #include "mruby/string.h" 36 | #include "mruby/variable.h" 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "mrb_pointer.h" 47 | 48 | #define DONE mrb_gc_arena_restore(mrb, 0); 49 | 50 | #define CREATE_REDIS_COMMAND_ARG1(argv, lens, cmd, arg1) \ 51 | argv[0] = cmd; \ 52 | argv[1] = RSTRING_PTR(arg1); \ 53 | lens[0] = strlen(cmd); \ 54 | lens[1] = RSTRING_LEN(arg1) 55 | #define CREATE_REDIS_COMMAND_ARG2(argv, lens, cmd, arg1, arg2) \ 56 | argv[0] = cmd; \ 57 | argv[1] = RSTRING_PTR(arg1); \ 58 | argv[2] = RSTRING_PTR(arg2); \ 59 | lens[0] = strlen(cmd); \ 60 | lens[1] = RSTRING_LEN(arg1); \ 61 | lens[2] = RSTRING_LEN(arg2) 62 | #define CREATE_REDIS_COMMAND_ARG3(argv, lens, cmd, arg1, arg2, arg3) \ 63 | argv[0] = cmd; \ 64 | argv[1] = RSTRING_PTR(arg1); \ 65 | argv[2] = RSTRING_PTR(arg2); \ 66 | argv[3] = RSTRING_PTR(arg3); \ 67 | lens[0] = strlen(cmd); \ 68 | lens[1] = RSTRING_LEN(arg1); \ 69 | lens[2] = RSTRING_LEN(arg2); \ 70 | lens[3] = RSTRING_LEN(arg3) 71 | 72 | typedef struct ReplyHandlingRule { 73 | mrb_bool status_to_symbol; 74 | mrb_bool integer_to_bool; 75 | mrb_bool emptyarray_to_nil; 76 | mrb_bool return_exception; 77 | } ReplyHandlingRule; 78 | 79 | #define DEFAULT_REPLY_HANDLING_RULE \ 80 | { \ 81 | .status_to_symbol = FALSE, .integer_to_bool = FALSE, .emptyarray_to_nil = FALSE, .return_exception = FALSE, \ 82 | } 83 | 84 | static inline mrb_value mrb_redis_get_reply(redisReply *reply, mrb_state *mrb, const ReplyHandlingRule *rule); 85 | static inline int mrb_redis_create_command_noarg(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens); 86 | static inline int mrb_redis_create_command_str(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens); 87 | static inline int mrb_redis_create_command_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens); 88 | static inline int mrb_redis_create_command_str_str(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens); 89 | static inline int mrb_redis_create_command_str_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens); 90 | static inline int mrb_redis_create_command_str_str_str(mrb_state *mrb, const char *cmd, const char **argv, 91 | size_t *lens); 92 | static inline int mrb_redis_create_command_str_str_int(mrb_state *mrb, const char *cmd, const char **argv, 93 | size_t *lens); 94 | static inline int mrb_redis_create_command_str_int_int(mrb_state *mrb, const char *cmd, const char **argv, 95 | size_t *lens); 96 | static inline int mrb_redis_create_command_str_float_str(mrb_state *mrb, const char *cmd, const char **argv, 97 | size_t *lens); 98 | static inline redisContext *mrb_redis_get_context(mrb_state *mrb, mrb_value self); 99 | static inline mrb_value mrb_redis_execute_command(mrb_state *mrb, mrb_value self, int argc, const char **argv, 100 | const size_t *lens, const ReplyHandlingRule *rule); 101 | 102 | static void redisContext_free(mrb_state *mrb, void *p) 103 | { 104 | redisFree(p); 105 | } 106 | 107 | static const struct mrb_data_type redisContext_type = { 108 | "redisContext", redisContext_free, 109 | }; 110 | 111 | static inline void mrb_redis_check_error(redisContext *context, mrb_state *mrb) 112 | { 113 | if (context->err != 0) { 114 | if (errno != 0) { 115 | mrb_sys_fail(mrb, context->errstr); 116 | } else { 117 | switch (context->err) { 118 | case REDIS_ERR_EOF: 119 | mrb_raise(mrb, E_EOF_ERROR, context->errstr); 120 | break; 121 | case REDIS_ERR_PROTOCOL: 122 | mrb_raise(mrb, E_REDIS_ERR_PROTOCOL, context->errstr); 123 | break; 124 | case REDIS_ERR_OOM: 125 | mrb_raise(mrb, E_REDIS_ERR_OOM, context->errstr); 126 | break; 127 | default: 128 | mrb_raise(mrb, E_REDIS_ERROR, context->errstr); 129 | } 130 | } 131 | } 132 | } 133 | 134 | static mrb_value mrb_redis_connect_set_raw(mrb_state *mrb, mrb_value self) 135 | { 136 | redisContext *rc; 137 | mrb_value host, port; 138 | mrb_int timeout = 1; 139 | struct timeval timeout_struct = {timeout, 0}; 140 | 141 | if (mrb_get_args(mrb, "oo|i", &host, &port, &timeout) == 3) { 142 | timeout_struct.tv_sec = timeout; 143 | } 144 | 145 | rc = redisConnectWithTimeout(mrb_str_to_cstr(mrb, host), mrb_fixnum(port), timeout_struct); 146 | if (rc->err) { 147 | mrb_raise(mrb, E_REDIS_ERROR, "redis connection failed."); 148 | } 149 | 150 | mrb_udptr_set(mrb, (void *)rc); 151 | 152 | return self; 153 | } 154 | 155 | static mrb_value mrb_redis_connect(mrb_state *mrb, mrb_value self) 156 | { 157 | mrb_value host, port; 158 | mrb_int timeout = 1; 159 | struct timeval timeout_struct = {timeout, 0}; 160 | mrb_int argc = 0; 161 | 162 | redisContext *rc = (redisContext *)DATA_PTR(self); 163 | if (rc) { 164 | redisFree(rc); 165 | } 166 | DATA_TYPE(self) = &redisContext_type; 167 | DATA_PTR(self) = NULL; 168 | 169 | argc = mrb_get_args(mrb, "|oo|i", &host, &port, &timeout); 170 | 171 | if (argc == 3) { 172 | timeout_struct.tv_sec = timeout; 173 | rc = redisConnectWithTimeout(mrb_str_to_cstr(mrb, host), mrb_fixnum(port), timeout_struct); 174 | } else if (argc == 2) { 175 | rc = redisConnectWithTimeout(mrb_str_to_cstr(mrb, host), mrb_fixnum(port), timeout_struct); 176 | } else if (argc == 0) { 177 | rc = (redisContext *)mrb_udptr_get(mrb); 178 | } 179 | 180 | if (rc->err) { 181 | redisFree(rc); 182 | mrb_raise(mrb, E_REDIS_ERROR, "redis connection failed."); 183 | } 184 | 185 | DATA_PTR(self) = rc; 186 | 187 | mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "keepalive"), mrb_symbol_value(mrb_intern_lit(mrb, "off"))); 188 | 189 | return self; 190 | } 191 | 192 | static mrb_value mrb_redis_enable_keepalive(mrb_state *mrb, mrb_value self) 193 | { 194 | redisContext *rc = mrb_redis_get_context(mrb, self); 195 | errno = 0; 196 | if (redisEnableKeepAlive(rc) == REDIS_OK) { 197 | mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "keepalive"), mrb_symbol_value(mrb_intern_lit(mrb, "on"))); 198 | } else { 199 | mrb_sys_fail(mrb, rc->errstr); 200 | } 201 | return mrb_nil_value(); 202 | } 203 | 204 | static mrb_value mrb_redis_keepalive(mrb_state *mrb, mrb_value self) 205 | { 206 | mrb_redis_get_context(mrb, self); // Check if closed 207 | return mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "keepalive")); 208 | } 209 | 210 | static mrb_value mrb_redis_host(mrb_state *mrb, mrb_value self) 211 | { 212 | redisContext *rc = mrb_redis_get_context(mrb, self); 213 | if (rc->connection_type != REDIS_CONN_TCP) { 214 | // Never expect to come here since mruby-redis does not support unix socket connection... 215 | return mrb_nil_value(); 216 | } 217 | return mrb_str_new_cstr(mrb, rc->tcp.host); 218 | } 219 | 220 | static mrb_value mrb_redis_port(mrb_state *mrb, mrb_value self) 221 | { 222 | redisContext *rc = mrb_redis_get_context(mrb, self); 223 | if (rc->connection_type != REDIS_CONN_TCP) { 224 | // Never expect to come here since mruby-redis does not support unix socket connection... 225 | return mrb_nil_value(); 226 | } 227 | return mrb_fixnum_value((mrb_int)(rc->tcp.port)); 228 | } 229 | 230 | static mrb_value mrb_redis_ping(mrb_state *mrb, mrb_value self) 231 | { 232 | const char *argv[1]; 233 | size_t lens[1]; 234 | int argc = mrb_redis_create_command_noarg(mrb, "PING", argv, lens); 235 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 236 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 237 | } 238 | 239 | static mrb_value mrb_redis_auth(mrb_state *mrb, mrb_value self) 240 | { 241 | const char *argv[2]; 242 | size_t lens[2]; 243 | int argc = mrb_redis_create_command_str(mrb, "AUTH", argv, lens); 244 | ReplyHandlingRule rule = {.return_exception = TRUE}; 245 | mrb_value reply = mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 246 | if (mrb_exception_p(reply)) { 247 | if (mrb_obj_is_instance_of(mrb, reply, E_REDIS_REPLY_ERROR)) { 248 | mrb_raisef(mrb, E_REDIS_ERR_AUTH, "incorrect password"); 249 | } else { 250 | mrb_exc_raise(mrb, reply); 251 | } 252 | } 253 | return reply; 254 | } 255 | 256 | static mrb_value mrb_redis_select(mrb_state *mrb, mrb_value self) 257 | { 258 | const char *argv[2]; 259 | size_t lens[2]; 260 | int argc = mrb_redis_create_command_int(mrb, "SELECT", argv, lens); 261 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 262 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 263 | } 264 | 265 | static mrb_value mrb_redis_set(mrb_state *mrb, mrb_value self) 266 | { 267 | mrb_value key, val, opt; 268 | mrb_bool b = 0; 269 | const char *argv[7]; 270 | size_t lens[7]; 271 | int c = 3; 272 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 273 | 274 | mrb_get_args(mrb, "SS|H?", &key, &val, &opt, &b); 275 | 276 | CREATE_REDIS_COMMAND_ARG2(argv, lens, "SET", key, val); 277 | if (b) { 278 | mrb_value ex = mrb_hash_delete_key(mrb, opt, mrb_str_new_cstr(mrb, "EX")); 279 | mrb_value px = mrb_hash_delete_key(mrb, opt, mrb_str_new_cstr(mrb, "PX")); 280 | mrb_bool nx = mrb_bool(mrb_hash_delete_key(mrb, opt, mrb_str_new_cstr(mrb, "NX"))); 281 | mrb_bool xx = mrb_bool(mrb_hash_delete_key(mrb, opt, mrb_str_new_cstr(mrb, "XX"))); 282 | 283 | if (!mrb_nil_p(ex) && !mrb_nil_p(px)) { 284 | mrb_raise(mrb, E_ARGUMENT_ERROR, "Only one of EX or PX can be set"); 285 | } 286 | 287 | if (nx && xx) { 288 | mrb_raise(mrb, E_ARGUMENT_ERROR, "Either NX or XX is true"); 289 | } 290 | 291 | if (!mrb_nil_p(ex)) { 292 | argv[c] = "EX"; 293 | lens[c] = strlen("EX"); 294 | c++; 295 | if (mrb_fixnum_p(ex)) { 296 | ex = mrb_fixnum_to_str(mrb, ex, 10); 297 | } 298 | if (!mrb_string_p(ex)) { 299 | mrb_raisef(mrb, E_TYPE_ERROR, "EX should be int or str, but %S given", ex); 300 | } 301 | argv[c] = RSTRING_PTR(ex); 302 | lens[c] = RSTRING_LEN(ex); 303 | c++; 304 | } 305 | 306 | if (!mrb_nil_p(px)) { 307 | argv[c] = "PX"; 308 | lens[c] = strlen("PX"); 309 | c++; 310 | if (mrb_fixnum_p(px)) { 311 | px = mrb_fixnum_to_str(mrb, px, 10); 312 | } 313 | if (!mrb_string_p(px)) { 314 | mrb_raisef(mrb, E_TYPE_ERROR, "PX should be int or str, but %S given", px); 315 | } 316 | argv[c] = RSTRING_PTR(px); 317 | lens[c] = RSTRING_LEN(px); 318 | c++; 319 | } 320 | 321 | if (nx) { 322 | argv[c] = "NX"; 323 | lens[c] = strlen("NX"); 324 | c++; 325 | } 326 | 327 | if (xx) { 328 | argv[c] = "XX"; 329 | lens[c] = strlen("XX"); 330 | c++; 331 | } 332 | 333 | if (!mrb_hash_empty_p(mrb, opt)) { 334 | mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown option(s) specified %S (note: only string can be key, not the symbol", 335 | mrb_hash_keys(mrb, opt)); 336 | } 337 | } 338 | 339 | return mrb_redis_execute_command(mrb, self, c, argv, lens, &rule); 340 | } 341 | 342 | static mrb_value mrb_redis_get(mrb_state *mrb, mrb_value self) 343 | { 344 | const char *argv[2]; 345 | size_t lens[2]; 346 | int argc = mrb_redis_create_command_str(mrb, "GET", argv, lens); 347 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 348 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 349 | } 350 | 351 | static mrb_value mrb_redis_keys(mrb_state *mrb, mrb_value self) 352 | { 353 | const char *argv[2]; 354 | size_t lens[2]; 355 | int argc = mrb_redis_create_command_str(mrb, "KEYS", argv, lens); 356 | ReplyHandlingRule rule = {.emptyarray_to_nil = TRUE}; 357 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 358 | } 359 | 360 | static mrb_value mrb_redis_exists(mrb_state *mrb, mrb_value self) 361 | { 362 | const char *argv[2]; 363 | size_t lens[2]; 364 | int argc = mrb_redis_create_command_str(mrb, "EXISTS", argv, lens); 365 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 366 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 367 | } 368 | 369 | static mrb_value mrb_redis_expire(mrb_state *mrb, mrb_value self) 370 | { 371 | const char *argv[3]; 372 | size_t lens[3]; 373 | int argc = mrb_redis_create_command_str_int(mrb, "EXPIRE", argv, lens); 374 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 375 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 376 | } 377 | 378 | static mrb_value mrb_redis_flushdb(mrb_state *mrb, mrb_value self) 379 | { 380 | const char *argv[1]; 381 | size_t lens[1]; 382 | int argc = mrb_redis_create_command_noarg(mrb, "FLUSHDB", argv, lens); 383 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 384 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 385 | } 386 | 387 | static mrb_value mrb_redis_flushall(mrb_state *mrb, mrb_value self) 388 | { 389 | const char *argv[1]; 390 | size_t lens[1]; 391 | int argc = mrb_redis_create_command_noarg(mrb, "FLUSHALL", argv, lens); 392 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 393 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 394 | } 395 | 396 | static mrb_value mrb_redis_randomkey(mrb_state *mrb, mrb_value self) 397 | { 398 | const char *argv[1]; 399 | size_t lens[1]; 400 | int argc = mrb_redis_create_command_noarg(mrb, "RANDOMKEY", argv, lens); 401 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 402 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 403 | } 404 | 405 | static mrb_value mrb_redis_del(mrb_state *mrb, mrb_value self) 406 | { 407 | const char *argv[2]; 408 | size_t lens[2]; 409 | int argc = mrb_redis_create_command_str(mrb, "DEL", argv, lens); 410 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 411 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 412 | } 413 | 414 | static mrb_value mrb_redis_incr(mrb_state *mrb, mrb_value self) 415 | { 416 | const char *argv[2]; 417 | size_t lens[2]; 418 | int argc = mrb_redis_create_command_str(mrb, "INCR", argv, lens); 419 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 420 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 421 | } 422 | 423 | static mrb_value mrb_redis_decr(mrb_state *mrb, mrb_value self) 424 | { 425 | const char *argv[2]; 426 | size_t lens[2]; 427 | int argc = mrb_redis_create_command_str(mrb, "DECR", argv, lens); 428 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 429 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 430 | } 431 | 432 | static mrb_value mrb_redis_incrby(mrb_state *mrb, mrb_value self) 433 | { 434 | const char *argv[3]; 435 | size_t lens[3]; 436 | int argc = mrb_redis_create_command_str_int(mrb, "INCRBY", argv, lens); 437 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 438 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 439 | } 440 | 441 | static mrb_value mrb_redis_decrby(mrb_state *mrb, mrb_value self) 442 | { 443 | const char *argv[3]; 444 | size_t lens[3]; 445 | int argc = mrb_redis_create_command_str_int(mrb, "DECRBY", argv, lens); 446 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 447 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 448 | } 449 | 450 | static mrb_value mrb_redis_llen(mrb_state *mrb, mrb_value self) 451 | { 452 | const char *argv[2]; 453 | size_t lens[2]; 454 | int argc = mrb_redis_create_command_str(mrb, "LLEN", argv, lens); 455 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 456 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 457 | } 458 | 459 | static mrb_value mrb_redis_rpush(mrb_state *mrb, mrb_value self) 460 | { 461 | const char *argv[3]; 462 | size_t lens[3]; 463 | int argc = mrb_redis_create_command_str_str(mrb, "RPUSH", argv, lens); 464 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 465 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 466 | } 467 | 468 | static mrb_value mrb_redis_lpush(mrb_state *mrb, mrb_value self) 469 | { 470 | const char *argv[3]; 471 | size_t lens[3]; 472 | int argc = mrb_redis_create_command_str_str(mrb, "LPUSH", argv, lens); 473 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 474 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 475 | } 476 | 477 | static mrb_value mrb_redis_rpop(mrb_state *mrb, mrb_value self) 478 | { 479 | const char *argv[2]; 480 | size_t lens[2]; 481 | int argc = mrb_redis_create_command_str(mrb, "RPOP", argv, lens); 482 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 483 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 484 | } 485 | 486 | static mrb_value mrb_redis_lpop(mrb_state *mrb, mrb_value self) 487 | { 488 | const char *argv[2]; 489 | size_t lens[2]; 490 | int argc = mrb_redis_create_command_str(mrb, "LPOP", argv, lens); 491 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 492 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 493 | } 494 | 495 | static mrb_value mrb_redis_lrange(mrb_state *mrb, mrb_value self) 496 | { 497 | const char *argv[4]; 498 | size_t lens[4]; 499 | int argc = mrb_redis_create_command_str_int_int(mrb, "LRANGE", argv, lens); 500 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 501 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 502 | } 503 | 504 | static mrb_value mrb_redis_ltrim(mrb_state *mrb, mrb_value self) 505 | { 506 | const char *argv[4]; 507 | size_t lens[4]; 508 | int argc = mrb_redis_create_command_str_int_int(mrb, "LTRIM", argv, lens); 509 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 510 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 511 | } 512 | 513 | static mrb_value mrb_redis_lindex(mrb_state *mrb, mrb_value self) 514 | { 515 | const char *argv[3]; 516 | size_t lens[3]; 517 | int argc = mrb_redis_create_command_str_int(mrb, "LINDEX", argv, lens); 518 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 519 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 520 | } 521 | 522 | static mrb_value mrb_redis_sadd(mrb_state *mrb, mrb_value self) 523 | { 524 | mrb_value key, *members; 525 | mrb_int members_len; 526 | const char **argv; 527 | size_t *lens; 528 | size_t argc; 529 | int i; 530 | 531 | mrb_get_args(mrb, "o*", &key, &members, &members_len); 532 | if (members_len == 0) { 533 | mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); 534 | } 535 | argc = 2 + members_len; 536 | 537 | argv = (const char **)alloca(argc * sizeof(char *)); 538 | lens = (size_t *)alloca(argc * sizeof(size_t)); 539 | 540 | CREATE_REDIS_COMMAND_ARG1(argv, lens, "SADD", key); 541 | for (i = 0; i < members_len; i++) { 542 | argv[i + 2] = RSTRING_PTR(members[i]); 543 | lens[i + 2] = RSTRING_LEN(members[i]); 544 | } 545 | 546 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 547 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 548 | } 549 | 550 | static mrb_value mrb_redis_srem(mrb_state *mrb, mrb_value self) 551 | { 552 | mrb_value key, *members; 553 | mrb_int members_len; 554 | const char **argv; 555 | size_t *lens, argc; 556 | int i; 557 | 558 | mrb_get_args(mrb, "o*", &key, &members, &members_len); 559 | if (members_len < 1) { 560 | mrb_raise(mrb, E_ARGUMENT_ERROR, "wrong number of arguments"); 561 | } 562 | argc = members_len + 2; 563 | 564 | argv = (const char **)alloca(argc * sizeof(char *)); 565 | lens = (size_t *)alloca(argc * sizeof(size_t)); 566 | 567 | CREATE_REDIS_COMMAND_ARG1(argv, lens, "SREM", key); 568 | for (i = 0; i < members_len; i++) { 569 | argv[i + 2] = RSTRING_PTR(members[i]); 570 | lens[i + 2] = RSTRING_LEN(members[i]); 571 | } 572 | 573 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 574 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 575 | } 576 | 577 | static mrb_value mrb_redis_sismember(mrb_state *mrb, mrb_value self) 578 | { 579 | const char *argv[3]; 580 | size_t lens[3]; 581 | int argc = mrb_redis_create_command_str_str(mrb, "SISMEMBER", argv, lens); 582 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 583 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 584 | } 585 | 586 | static mrb_value mrb_redis_smembers(mrb_state *mrb, mrb_value self) 587 | { 588 | const char *argv[2]; 589 | size_t lens[2]; 590 | int argc = mrb_redis_create_command_str(mrb, "SMEMBERS", argv, lens); 591 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 592 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 593 | } 594 | 595 | static mrb_value mrb_redis_scard(mrb_state *mrb, mrb_value self) 596 | { 597 | const char *argv[2]; 598 | size_t lens[2]; 599 | int argc = mrb_redis_create_command_str(mrb, "SCARD", argv, lens); 600 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 601 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 602 | } 603 | 604 | static mrb_value mrb_redis_spop(mrb_state *mrb, mrb_value self) 605 | { 606 | const char *argv[2]; 607 | size_t lens[2]; 608 | int argc = mrb_redis_create_command_str(mrb, "SPOP", argv, lens); 609 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 610 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 611 | } 612 | 613 | static mrb_value mrb_redis_hset(mrb_state *mrb, mrb_value self) 614 | { 615 | const char *argv[4]; 616 | size_t lens[4]; 617 | int argc = mrb_redis_create_command_str_str_str(mrb, "HSET", argv, lens); 618 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 619 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 620 | } 621 | 622 | static mrb_value mrb_redis_hsetnx(mrb_state *mrb, mrb_value self) 623 | { 624 | const char *argv[4]; 625 | size_t lens[4]; 626 | int argc = mrb_redis_create_command_str_str_str(mrb, "HSETNX", argv, lens); 627 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 628 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 629 | } 630 | 631 | static mrb_value mrb_redis_hget(mrb_state *mrb, mrb_value self) 632 | { 633 | const char *argv[3]; 634 | size_t lens[3]; 635 | int argc = mrb_redis_create_command_str_str(mrb, "HGET", argv, lens); 636 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 637 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 638 | } 639 | 640 | static mrb_value mrb_redis_hgetall(mrb_state *mrb, mrb_value self) 641 | { 642 | const char *argv[2]; 643 | size_t lens[2]; 644 | int argc = mrb_redis_create_command_str(mrb, "HGETALL", argv, lens); 645 | ReplyHandlingRule rule = {.emptyarray_to_nil = TRUE}; 646 | mrb_value reply = mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 647 | if (mrb_array_p(reply)) { 648 | // Convert [k1, v1, ..., kN, vN] --> {k1 => v1, ..., kN =>} 649 | mrb_value hash = mrb_hash_new_capa(mrb, RARRAY_LEN(reply) / 2); 650 | for (mrb_int i = 0; i < RARRAY_LEN(reply); i += 2) { 651 | mrb_hash_set(mrb, hash, mrb_ary_ref(mrb, reply, i), mrb_ary_ref(mrb, reply, i + 1)); 652 | } 653 | return hash; 654 | } else { 655 | return reply; 656 | } 657 | } 658 | 659 | static mrb_value mrb_redis_hdel(mrb_state *mrb, mrb_value self) 660 | { 661 | const char *argv[3]; 662 | size_t lens[3]; 663 | int argc = mrb_redis_create_command_str_str(mrb, "HDEL", argv, lens); 664 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 665 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 666 | } 667 | 668 | static mrb_value mrb_redis_hexists(mrb_state *mrb, mrb_value self) 669 | { 670 | const char *argv[3]; 671 | size_t lens[3]; 672 | int argc = mrb_redis_create_command_str_str(mrb, "HEXISTS", argv, lens); 673 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 674 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 675 | } 676 | 677 | static mrb_value mrb_redis_hkeys(mrb_state *mrb, mrb_value self) 678 | { 679 | const char *argv[2]; 680 | size_t lens[2]; 681 | int argc = mrb_redis_create_command_str(mrb, "HKEYS", argv, lens); 682 | ReplyHandlingRule rule = {.emptyarray_to_nil = TRUE}; 683 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 684 | } 685 | 686 | static mrb_value mrb_redis_hmget(mrb_state *mrb, mrb_value self) 687 | { 688 | mrb_value *mrb_argv, array; 689 | mrb_int argc = 0; 690 | int i, ai; 691 | const char **argv; 692 | size_t *argvlen; 693 | mrb_int argc_current; 694 | redisContext *rc; 695 | redisReply *rr; 696 | 697 | mrb_get_args(mrb, "*", &mrb_argv, &argc); 698 | argc++; 699 | 700 | argv = (const char **)alloca(argc * sizeof(char *)); 701 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 702 | 703 | argv[0] = "HMGET"; 704 | argvlen[0] = sizeof("HMGET") - 1; 705 | 706 | ai = mrb_gc_arena_save(mrb); 707 | for (argc_current = 1; argc_current < argc; argc_current++) { 708 | mrb_value curr = mrb_str_to_str(mrb, mrb_argv[argc_current - 1]); 709 | argv[argc_current] = RSTRING_PTR(curr); 710 | argvlen[argc_current] = RSTRING_LEN(curr); 711 | mrb_gc_arena_restore(mrb, ai); 712 | } 713 | 714 | array = mrb_nil_value(); 715 | rc = mrb_redis_get_context(mrb, self); 716 | rr = redisCommandArgv(rc, argc, argv, argvlen); 717 | if (rc->err) { 718 | mrb_redis_check_error(rc, mrb); 719 | } 720 | if (rr->type == REDIS_REPLY_ARRAY) { 721 | if (rr->elements > 0) { 722 | array = mrb_ary_new(mrb); 723 | 724 | for (i = 0; i < rr->elements; i++) { 725 | if (rr->element[i]->len > 0) { 726 | mrb_ary_push(mrb, array, mrb_str_new(mrb, rr->element[i]->str, rr->element[i]->len)); 727 | } else { 728 | mrb_ary_push(mrb, array, mrb_nil_value()); 729 | } 730 | } 731 | } 732 | } else { 733 | mrb_value msg = mrb_str_new_cstr(mrb, rr->str); 734 | freeReplyObject(rr); 735 | mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S", msg); 736 | } 737 | 738 | freeReplyObject(rr); 739 | return array; 740 | } 741 | 742 | static mrb_value mrb_redis_hmset(mrb_state *mrb, mrb_value self) 743 | { 744 | mrb_value *mrb_argv; 745 | mrb_int argc = 0; 746 | redisContext *rc = mrb_redis_get_context(mrb, self); 747 | redisReply *rr; 748 | const char **argv; 749 | size_t *argvlen; 750 | int ai; 751 | mrb_int argc_current; 752 | 753 | mrb_get_args(mrb, "*", &mrb_argv, &argc); 754 | argc++; 755 | argv = (const char **)alloca(argc * sizeof(char *)); 756 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 757 | 758 | argv[0] = "HMSET"; 759 | argvlen[0] = sizeof("HMSET") - 1; 760 | 761 | ai = mrb_gc_arena_save(mrb); 762 | for (argc_current = 1; argc_current < argc; argc_current++) { 763 | mrb_value curr = mrb_str_to_str(mrb, mrb_argv[argc_current - 1]); 764 | argv[argc_current] = RSTRING_PTR(curr); 765 | argvlen[argc_current] = RSTRING_LEN(curr); 766 | mrb_gc_arena_restore(mrb, ai); 767 | } 768 | 769 | rr = redisCommandArgv(rc, argc, argv, argvlen); 770 | if (rc->err) { 771 | mrb_redis_check_error(rc, mrb); 772 | } 773 | if (rr->type == REDIS_REPLY_STATUS && rr != NULL) { 774 | freeReplyObject(rr); 775 | } else { 776 | mrb_value msg = mrb_str_new_cstr(mrb, rr->str); 777 | freeReplyObject(rr); 778 | mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S", msg); 779 | } 780 | 781 | return self; 782 | } 783 | 784 | static mrb_value mrb_redis_hvals(mrb_state *mrb, mrb_value self) 785 | { 786 | const char *argv[2]; 787 | size_t lens[2]; 788 | int argc = mrb_redis_create_command_str(mrb, "HVALS", argv, lens); 789 | ReplyHandlingRule rule = {.emptyarray_to_nil = TRUE}; 790 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 791 | } 792 | 793 | static mrb_value mrb_redis_hincrby(mrb_state *mrb, mrb_value self) 794 | { 795 | const char *argv[4]; 796 | size_t lens[4]; 797 | int argc = mrb_redis_create_command_str_str_int(mrb, "HINCRBY", argv, lens); 798 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 799 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 800 | } 801 | 802 | static mrb_value mrb_redis_mset(mrb_state *mrb, mrb_value self) 803 | { 804 | mrb_value *mrb_argv; 805 | mrb_int argc = 0; 806 | redisContext *rc = mrb_redis_get_context(mrb, self); 807 | redisReply *rr; 808 | const char **argv; 809 | size_t *argvlen; 810 | int ai; 811 | mrb_int argc_current; 812 | 813 | mrb_get_args(mrb, "*", &mrb_argv, &argc); 814 | argc++; 815 | argv = (const char **)alloca(argc * sizeof(char *)); 816 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 817 | 818 | argv[0] = "MSET"; 819 | argvlen[0] = sizeof("MSET") - 1; 820 | 821 | ai = mrb_gc_arena_save(mrb); 822 | for (argc_current = 1; argc_current < argc; argc_current++) { 823 | mrb_value curr = mrb_str_to_str(mrb, mrb_argv[argc_current - 1]); 824 | argv[argc_current] = RSTRING_PTR(curr); 825 | argvlen[argc_current] = RSTRING_LEN(curr); 826 | mrb_gc_arena_restore(mrb, ai); 827 | } 828 | 829 | rr = redisCommandArgv(rc, argc, argv, argvlen); 830 | if (rc->err) { 831 | mrb_redis_check_error(rc, mrb); 832 | } 833 | if (rr->type == REDIS_REPLY_STATUS && rr != NULL) { 834 | freeReplyObject(rr); 835 | } else { 836 | mrb_value msg = mrb_str_new_cstr(mrb, rr->str); 837 | freeReplyObject(rr); 838 | mrb_raisef(mrb, E_ARGUMENT_ERROR, "%S", msg); 839 | } 840 | 841 | return self; 842 | } 843 | 844 | static mrb_value mrb_redis_mget(mrb_state *mrb, mrb_value self) 845 | { 846 | mrb_value *mrb_argv, array; 847 | mrb_int argc = 0; 848 | int i, ai; 849 | const char **argv; 850 | size_t *argvlen; 851 | mrb_int argc_current; 852 | redisContext *rc; 853 | redisReply *rr; 854 | 855 | mrb_get_args(mrb, "*", &mrb_argv, &argc); 856 | argc++; 857 | 858 | argv = (const char **)alloca(argc * sizeof(char *)); 859 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 860 | 861 | argv[0] = "MGET"; 862 | argvlen[0] = sizeof("MGET") - 1; 863 | 864 | ai = mrb_gc_arena_save(mrb); 865 | for (argc_current = 1; argc_current < argc; argc_current++) { 866 | mrb_value curr = mrb_str_to_str(mrb, mrb_argv[argc_current - 1]); 867 | argv[argc_current] = RSTRING_PTR(curr); 868 | argvlen[argc_current] = RSTRING_LEN(curr); 869 | mrb_gc_arena_restore(mrb, ai); 870 | } 871 | 872 | array = mrb_nil_value(); 873 | rc = mrb_redis_get_context(mrb, self); 874 | rr = redisCommandArgv(rc, argc, argv, argvlen); 875 | if (rc->err) { 876 | mrb_redis_check_error(rc, mrb); 877 | } 878 | if (rr->type == REDIS_REPLY_ARRAY) { 879 | if (rr->elements > 0) { 880 | array = mrb_ary_new(mrb); 881 | 882 | for (i = 0; i < rr->elements; i++) { 883 | if (rr->element[i]->len > 0) { 884 | mrb_ary_push(mrb, array, mrb_str_new(mrb, rr->element[i]->str, rr->element[i]->len)); 885 | } else { 886 | mrb_ary_push(mrb, array, mrb_nil_value()); 887 | } 888 | } 889 | } 890 | } else { 891 | char const *msg = rr->str; 892 | freeReplyObject(rr); 893 | mrb_raise(mrb, E_ARGUMENT_ERROR, msg); 894 | } 895 | 896 | freeReplyObject(rr); 897 | return array; 898 | } 899 | 900 | 901 | 902 | static mrb_value mrb_redis_ttl(mrb_state *mrb, mrb_value self) 903 | { 904 | const char *argv[2]; 905 | size_t lens[2]; 906 | int argc = mrb_redis_create_command_str(mrb, "TTL", argv, lens); 907 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 908 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 909 | } 910 | 911 | static mrb_value mrb_redis_zadd(mrb_state *mrb, mrb_value self) 912 | { 913 | const char *argv[4]; 914 | size_t lens[4]; 915 | int argc = mrb_redis_create_command_str_float_str(mrb, "ZADD", argv, lens); 916 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 917 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 918 | } 919 | 920 | static mrb_value mrb_redis_zcard(mrb_state *mrb, mrb_value self) 921 | { 922 | const char *argv[2]; 923 | size_t lens[2]; 924 | int argc = mrb_redis_create_command_str(mrb, "ZCARD", argv, lens); 925 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 926 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 927 | } 928 | 929 | static mrb_value mrb_redis_basic_zrange(mrb_state *mrb, mrb_value self, const char *cmd) 930 | { 931 | const char *argv[4]; 932 | size_t lens[4]; 933 | int argc = mrb_redis_create_command_str_int_int(mrb, cmd, argv, lens); 934 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 935 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 936 | } 937 | 938 | static mrb_value mrb_redis_zrange(mrb_state *mrb, mrb_value self) 939 | { 940 | return mrb_redis_basic_zrange(mrb, self, "ZRANGE"); 941 | } 942 | 943 | static mrb_value mrb_redis_zrevrange(mrb_state *mrb, mrb_value self) 944 | { 945 | return mrb_redis_basic_zrange(mrb, self, "ZREVRANGE"); 946 | } 947 | 948 | static mrb_value mrb_redis_basic_zrank(mrb_state *mrb, mrb_value self, const char *cmd) 949 | { 950 | const char *argv[3]; 951 | size_t lens[3]; 952 | int argc = mrb_redis_create_command_str_str(mrb, cmd, argv, lens); 953 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 954 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 955 | } 956 | 957 | static mrb_value mrb_redis_zrank(mrb_state *mrb, mrb_value self) 958 | { 959 | return mrb_redis_basic_zrank(mrb, self, "ZRANK"); 960 | } 961 | 962 | static mrb_value mrb_redis_zrevrank(mrb_state *mrb, mrb_value self) 963 | { 964 | return mrb_redis_basic_zrank(mrb, self, "ZREVRANK"); 965 | } 966 | 967 | static mrb_value mrb_redis_zscore(mrb_state *mrb, mrb_value self) 968 | { 969 | const char *argv[3]; 970 | size_t lens[3]; 971 | int argc = mrb_redis_create_command_str_str(mrb, "ZSCORE", argv, lens); 972 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 973 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 974 | } 975 | 976 | static mrb_value mrb_redis_pub(mrb_state *mrb, mrb_value self) 977 | { 978 | const char *argv[3]; 979 | size_t lens[3]; 980 | int argc = mrb_redis_create_command_str_str(mrb, "PUBLISH", argv, lens); 981 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 982 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 983 | } 984 | 985 | static mrb_value mrb_redis_pfadd(mrb_state *mrb, mrb_value self) 986 | { 987 | mrb_value key, *mrb_rest_argv; 988 | mrb_int argc = 0, rest_argc = 0; 989 | const char **argv; 990 | size_t *argvlen; 991 | 992 | mrb_get_args(mrb, "o*", &key, &mrb_rest_argv, &rest_argc); 993 | argc = rest_argc + 2; 994 | 995 | argv = (const char **)alloca(argc * sizeof(char *)); 996 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 997 | 998 | CREATE_REDIS_COMMAND_ARG1(argv, argvlen, "PFADD", key); 999 | 1000 | if (argc > 2) { 1001 | int ai = mrb_gc_arena_save(mrb); 1002 | mrb_int argc_current; 1003 | for (argc_current = 2; argc_current < argc; argc_current++) { 1004 | mrb_value curr = mrb_str_to_str(mrb, mrb_rest_argv[argc_current - 2]); 1005 | argv[argc_current] = RSTRING_PTR(curr); 1006 | argvlen[argc_current] = RSTRING_LEN(curr); 1007 | mrb_gc_arena_restore(mrb, ai); 1008 | } 1009 | } 1010 | 1011 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1012 | return mrb_redis_execute_command(mrb, self, argc, argv, argvlen, &rule); 1013 | } 1014 | 1015 | static mrb_value mrb_redis_pfcount(mrb_state *mrb, mrb_value self) 1016 | { 1017 | mrb_value key, *mrb_rest_argv; 1018 | mrb_int argc = 0, rest_argc = 0; 1019 | const char **argv; 1020 | size_t *argvlen; 1021 | 1022 | mrb_get_args(mrb, "o*", &key, &mrb_rest_argv, &rest_argc); 1023 | argc = rest_argc + 2; 1024 | 1025 | argv = (const char **)alloca(argc * sizeof(char *)); 1026 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 1027 | 1028 | CREATE_REDIS_COMMAND_ARG1(argv, argvlen, "PFCOUNT", key); 1029 | 1030 | if (argc > 2) { 1031 | int ai = mrb_gc_arena_save(mrb); 1032 | mrb_int argc_current; 1033 | for (argc_current = 2; argc_current < argc; argc_current++) { 1034 | mrb_value curr = mrb_str_to_str(mrb, mrb_rest_argv[argc_current - 2]); 1035 | argv[argc_current] = RSTRING_PTR(curr); 1036 | argvlen[argc_current] = RSTRING_LEN(curr); 1037 | mrb_gc_arena_restore(mrb, ai); 1038 | } 1039 | } 1040 | 1041 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1042 | return mrb_redis_execute_command(mrb, self, argc, argv, argvlen, &rule); 1043 | } 1044 | 1045 | static mrb_value mrb_redis_pfmerge(mrb_state *mrb, mrb_value self) 1046 | { 1047 | mrb_value dest_struct, src_struct, *mrb_rest_argv; 1048 | mrb_int argc = 0, rest_argc = 0; 1049 | const char **argv; 1050 | size_t *argvlen; 1051 | 1052 | mrb_get_args(mrb, "oo*", &dest_struct, &src_struct, &mrb_rest_argv, &rest_argc); 1053 | argc = rest_argc + 3; 1054 | 1055 | argv = (const char **)alloca(argc * sizeof(char *)); 1056 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 1057 | 1058 | CREATE_REDIS_COMMAND_ARG2(argv, argvlen, "PFMERGE", dest_struct, src_struct); 1059 | 1060 | if (argc > 3) { 1061 | int ai = mrb_gc_arena_save(mrb); 1062 | mrb_int argc_current; 1063 | for (argc_current = 3; argc_current < argc; argc_current++) { 1064 | mrb_value curr = mrb_str_to_str(mrb, mrb_rest_argv[argc_current - 3]); 1065 | argv[argc_current] = RSTRING_PTR(curr); 1066 | argvlen[argc_current] = RSTRING_LEN(curr); 1067 | mrb_gc_arena_restore(mrb, ai); 1068 | } 1069 | } 1070 | 1071 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1072 | return mrb_redis_execute_command(mrb, self, argc, argv, argvlen, &rule); 1073 | } 1074 | 1075 | static mrb_value mrb_redis_close(mrb_state *mrb, mrb_value self) 1076 | { 1077 | redisContext *rc = DATA_PTR(self); 1078 | 1079 | redisFree(rc); 1080 | 1081 | DATA_PTR(self) = NULL; 1082 | DATA_TYPE(self) = NULL; 1083 | 1084 | return mrb_nil_value(); 1085 | } 1086 | 1087 | static inline mrb_value mrb_redis_get_ary_reply(redisReply *reply, mrb_state *mrb, const ReplyHandlingRule *rule); 1088 | 1089 | static inline mrb_value mrb_redis_get_reply(redisReply *reply, mrb_state *mrb, const ReplyHandlingRule *rule) 1090 | { 1091 | switch (reply->type) { 1092 | case REDIS_REPLY_STRING: 1093 | return mrb_str_new(mrb, reply->str, reply->len); 1094 | break; 1095 | case REDIS_REPLY_ARRAY: 1096 | return mrb_redis_get_ary_reply(reply, mrb, rule); 1097 | break; 1098 | case REDIS_REPLY_INTEGER: { 1099 | if (rule->integer_to_bool) 1100 | return mrb_bool_value(reply->integer); 1101 | else if (FIXABLE(reply->integer)) 1102 | return mrb_fixnum_value(reply->integer); 1103 | else 1104 | return mrb_float_value(mrb, reply->integer); 1105 | } break; 1106 | case REDIS_REPLY_NIL: 1107 | return mrb_nil_value(); 1108 | break; 1109 | case REDIS_REPLY_STATUS: { 1110 | if (rule->status_to_symbol) { 1111 | mrb_sym status = mrb_intern(mrb, reply->str, reply->len); 1112 | return mrb_symbol_value(status); 1113 | } else { 1114 | return mrb_str_new(mrb, reply->str, reply->len); 1115 | } 1116 | } break; 1117 | case REDIS_REPLY_ERROR: { 1118 | mrb_value err = mrb_str_new(mrb, reply->str, reply->len); 1119 | mrb_value exc = mrb_exc_new_str(mrb, E_REDIS_REPLY_ERROR, err); 1120 | if (rule->return_exception) { 1121 | return exc; 1122 | } else { 1123 | freeReplyObject(reply); 1124 | mrb_exc_raise(mrb, exc); 1125 | } 1126 | } break; 1127 | default: 1128 | freeReplyObject(reply); 1129 | mrb_raise(mrb, E_REDIS_ERROR, "unknown reply type"); 1130 | } 1131 | } 1132 | 1133 | static inline mrb_value mrb_redis_get_ary_reply(redisReply *reply, mrb_state *mrb, const ReplyHandlingRule *rule) 1134 | { 1135 | if (rule->emptyarray_to_nil && reply->elements == 0) { 1136 | return mrb_nil_value(); 1137 | } 1138 | mrb_value ary = mrb_ary_new_capa(mrb, reply->elements); 1139 | int ai = mrb_gc_arena_save(mrb); 1140 | size_t element_couter; 1141 | for (element_couter = 0; element_couter < reply->elements; element_couter++) { 1142 | mrb_value element = mrb_redis_get_reply(reply->element[element_couter], mrb, rule); 1143 | mrb_ary_push(mrb, ary, element); 1144 | mrb_gc_arena_restore(mrb, ai); 1145 | } 1146 | return ary; 1147 | } 1148 | 1149 | static mrb_value mrb_redisAppendCommandArgv(mrb_state *mrb, mrb_value self) 1150 | { 1151 | mrb_sym command; 1152 | mrb_value *mrb_argv; 1153 | mrb_int argc = 0, argc_current; 1154 | const char **argv; 1155 | size_t *argvlen; 1156 | mrb_int command_len; 1157 | mrb_sym queue_counter_sym; 1158 | mrb_value queue_counter_val; 1159 | mrb_int queue_counter; 1160 | redisContext *context; 1161 | int rc; 1162 | 1163 | mrb_get_args(mrb, "n*", &command, &mrb_argv, &argc); 1164 | argc++; 1165 | 1166 | argv = (const char **)alloca(argc * sizeof(char *)); 1167 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 1168 | 1169 | argv[0] = mrb_sym2name_len(mrb, command, &command_len); 1170 | argvlen[0] = command_len; 1171 | 1172 | for (argc_current = 1; argc_current < argc; argc_current++) { 1173 | mrb_value curr = mrb_str_to_str(mrb, mrb_argv[argc_current - 1]); 1174 | argv[argc_current] = RSTRING_PTR(curr); 1175 | argvlen[argc_current] = RSTRING_LEN(curr); 1176 | } 1177 | 1178 | queue_counter_sym = mrb_intern_lit(mrb, "queue_counter"); 1179 | queue_counter_val = mrb_iv_get(mrb, self, queue_counter_sym); 1180 | queue_counter = 1; 1181 | if (mrb_fixnum_p(queue_counter_val)) { 1182 | queue_counter = mrb_fixnum(queue_counter_val); 1183 | if (mrb_int_add_overflow(queue_counter, 1, &queue_counter)) { 1184 | mrb_raise(mrb, E_RUNTIME_ERROR, "integer addition would overflow"); 1185 | } 1186 | } 1187 | 1188 | context = mrb_redis_get_context(mrb, self); 1189 | errno = 0; 1190 | rc = redisAppendCommandArgv(context, argc, argv, argvlen); 1191 | if (rc == REDIS_OK) { 1192 | mrb_iv_set(mrb, self, queue_counter_sym, mrb_fixnum_value(queue_counter)); 1193 | } else { 1194 | mrb_redis_check_error(context, mrb); 1195 | } 1196 | 1197 | return self; 1198 | } 1199 | 1200 | static mrb_value mrb_redisGetReply(mrb_state *mrb, mrb_value self) 1201 | { 1202 | mrb_sym queue_counter_sym = mrb_intern_lit(mrb, "queue_counter"); 1203 | mrb_value queue_counter_val = mrb_iv_get(mrb, self, queue_counter_sym); 1204 | mrb_int queue_counter = -1; 1205 | redisContext *context; 1206 | redisReply *reply = NULL; 1207 | mrb_value reply_val; 1208 | ReplyHandlingRule rule = { 1209 | .status_to_symbol = TRUE, 1210 | .return_exception = TRUE, 1211 | }; 1212 | int rc; 1213 | 1214 | if (mrb_fixnum_p(queue_counter_val)) { 1215 | queue_counter = mrb_fixnum(queue_counter_val); 1216 | } 1217 | 1218 | context = mrb_redis_get_context(mrb, self); 1219 | reply_val = self; 1220 | errno = 0; 1221 | rc = redisGetReply(context, (void **)&reply); 1222 | if (rc == REDIS_OK && reply != NULL) { 1223 | struct mrb_jmpbuf *prev_jmp = mrb->jmp; 1224 | struct mrb_jmpbuf c_jmp; 1225 | 1226 | MRB_TRY(&c_jmp) 1227 | { 1228 | mrb->jmp = &c_jmp; 1229 | reply_val = mrb_redis_get_reply(reply, mrb, &rule); 1230 | if (queue_counter > 1) { 1231 | mrb_iv_set(mrb, self, queue_counter_sym, mrb_fixnum_value(--queue_counter)); 1232 | } else { 1233 | mrb_iv_remove(mrb, self, queue_counter_sym); 1234 | } 1235 | mrb->jmp = prev_jmp; 1236 | } 1237 | MRB_CATCH(&c_jmp) 1238 | { 1239 | mrb->jmp = prev_jmp; 1240 | freeReplyObject(reply); 1241 | MRB_THROW(mrb->jmp); 1242 | } 1243 | MRB_END_EXC(&c_jmp); 1244 | 1245 | freeReplyObject(reply); 1246 | } else { 1247 | mrb_redis_check_error(context, mrb); 1248 | } 1249 | 1250 | return reply_val; 1251 | } 1252 | 1253 | static mrb_value mrb_redisGetBulkReply(mrb_state *mrb, mrb_value self) 1254 | { 1255 | mrb_value queue_counter_val = mrb_iv_get(mrb, self, mrb_intern_lit(mrb, "queue_counter")); 1256 | mrb_int queue_counter; 1257 | mrb_value bulk_reply; 1258 | int ai; 1259 | 1260 | if (!mrb_fixnum_p(queue_counter_val)) 1261 | mrb_raise(mrb, E_RUNTIME_ERROR, "nothing queued yet"); 1262 | 1263 | queue_counter = mrb_fixnum(queue_counter_val); 1264 | bulk_reply = mrb_ary_new_capa(mrb, queue_counter); 1265 | ai = mrb_gc_arena_save(mrb); 1266 | 1267 | do { 1268 | mrb_value reply = mrb_redisGetReply(mrb, self); 1269 | mrb_ary_push(mrb, bulk_reply, reply); 1270 | mrb_gc_arena_restore(mrb, ai); 1271 | } while (--queue_counter > 0); 1272 | 1273 | return bulk_reply; 1274 | } 1275 | 1276 | static mrb_value mrb_redis_multi(mrb_state *mrb, mrb_value self) 1277 | { 1278 | const char *argv[1]; 1279 | size_t lens[1]; 1280 | int argc = mrb_redis_create_command_noarg(mrb, "MULTI", argv, lens); 1281 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1282 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1283 | } 1284 | 1285 | static mrb_value mrb_redis_exec(mrb_state *mrb, mrb_value self) 1286 | { 1287 | const char *argv[1]; 1288 | size_t lens[1]; 1289 | int argc = mrb_redis_create_command_noarg(mrb, "EXEC", argv, lens); 1290 | ReplyHandlingRule rule = {.emptyarray_to_nil = TRUE}; 1291 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1292 | } 1293 | 1294 | static mrb_value mrb_redis_discard(mrb_state *mrb, mrb_value self) 1295 | { 1296 | const char *argv[1]; 1297 | size_t lens[1]; 1298 | int argc = mrb_redis_create_command_noarg(mrb, "DISCARD", argv, lens); 1299 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1300 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1301 | } 1302 | 1303 | static mrb_value mrb_redis_watch(mrb_state *mrb, mrb_value self) 1304 | { 1305 | mrb_int argc = 0, rest_argc = 0; 1306 | mrb_value key, *mrb_rest_argv; 1307 | const char **argv; 1308 | size_t *argvlen; 1309 | 1310 | mrb_get_args(mrb, "o*", &key, &mrb_rest_argv, &rest_argc); 1311 | argc = rest_argc + 2; 1312 | 1313 | argv = (const char **)alloca(argc * sizeof(char *)); 1314 | argvlen = (size_t *)alloca(argc * sizeof(size_t)); 1315 | 1316 | CREATE_REDIS_COMMAND_ARG1(argv, argvlen, "WATCH", key); 1317 | 1318 | if (argc > 2) { 1319 | int ai = mrb_gc_arena_save(mrb); 1320 | mrb_int argc_current; 1321 | for (argc_current = 2; argc_current < argc; argc_current++) { 1322 | mrb_value curr = mrb_str_to_str(mrb, mrb_rest_argv[argc_current - 2]); 1323 | argv[argc_current] = RSTRING_PTR(curr); 1324 | argvlen[argc_current] = RSTRING_LEN(curr); 1325 | mrb_gc_arena_restore(mrb, ai); 1326 | } 1327 | } 1328 | 1329 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1330 | return mrb_redis_execute_command(mrb, self, argc, argv, argvlen, &rule); 1331 | } 1332 | 1333 | static mrb_value mrb_redis_unwatch(mrb_state *mrb, mrb_value self) 1334 | { 1335 | const char *argv[1]; 1336 | size_t lens[1]; 1337 | int argc = mrb_redis_create_command_noarg(mrb, "UNWATCH", argv, lens); 1338 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1339 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1340 | } 1341 | 1342 | static mrb_value mrb_redis_setnx(mrb_state *mrb, mrb_value self) 1343 | { 1344 | const char *argv[3]; 1345 | size_t lens[3]; 1346 | int argc = mrb_redis_create_command_str_str(mrb, "SETNX", argv, lens); 1347 | ReplyHandlingRule rule = {.integer_to_bool = TRUE}; 1348 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1349 | } 1350 | 1351 | static mrb_value mrb_redis_cluster(mrb_state *mrb, mrb_value self) 1352 | { 1353 | const char *argv[2]; 1354 | size_t lens[2]; 1355 | int argc = mrb_redis_create_command_str(mrb, "CLUSTER", argv, lens); 1356 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1357 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1358 | } 1359 | 1360 | static mrb_value mrb_redis_asking(mrb_state *mrb, mrb_value self) 1361 | { 1362 | const char *argv[1]; 1363 | size_t lens[1]; 1364 | int argc = mrb_redis_create_command_noarg(mrb, "ASKING", argv, lens); 1365 | ReplyHandlingRule rule = DEFAULT_REPLY_HANDLING_RULE; 1366 | return mrb_redis_execute_command(mrb, self, argc, argv, lens, &rule); 1367 | } 1368 | 1369 | static inline int mrb_redis_create_command_noarg(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1370 | { 1371 | argv[0] = cmd; 1372 | lens[0] = strlen(cmd); 1373 | return 1; 1374 | } 1375 | static inline int mrb_redis_create_command_str(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1376 | { 1377 | mrb_value str1; 1378 | mrb_get_args(mrb, "S", &str1); 1379 | CREATE_REDIS_COMMAND_ARG1(argv, lens, cmd, str1); 1380 | return 2; 1381 | } 1382 | static inline int mrb_redis_create_command_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1383 | { 1384 | mrb_value str1; 1385 | mrb_int int1; 1386 | mrb_get_args(mrb, "i", &int1); 1387 | str1 = mrb_fixnum_to_str(mrb, mrb_fixnum_value(int1), 10); 1388 | CREATE_REDIS_COMMAND_ARG1(argv, lens, cmd, str1); 1389 | return 2; 1390 | } 1391 | static inline int mrb_redis_create_command_str_str(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1392 | { 1393 | mrb_value str1, str2; 1394 | mrb_get_args(mrb, "SS", &str1, &str2); 1395 | CREATE_REDIS_COMMAND_ARG2(argv, lens, cmd, str1, str2); 1396 | return 3; 1397 | } 1398 | static inline int mrb_redis_create_command_str_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1399 | { 1400 | mrb_value str1, str2; 1401 | mrb_int int2; 1402 | mrb_get_args(mrb, "Si", &str1, &int2); 1403 | str2 = mrb_fixnum_to_str(mrb, mrb_fixnum_value(int2), 10); 1404 | CREATE_REDIS_COMMAND_ARG2(argv, lens, cmd, str1, str2); 1405 | return 3; 1406 | } 1407 | static inline int mrb_redis_create_command_str_str_str(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1408 | { 1409 | mrb_value str1, str2, str3; 1410 | mrb_get_args(mrb, "SSS", &str1, &str2, &str3); 1411 | CREATE_REDIS_COMMAND_ARG3(argv, lens, cmd, str1, str2, str3); 1412 | return 4; 1413 | } 1414 | static inline int mrb_redis_create_command_str_str_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1415 | { 1416 | mrb_value str1, str2, str3; 1417 | mrb_int int3; 1418 | mrb_get_args(mrb, "SSi", &str1, &str2, &int3); 1419 | str3 = mrb_fixnum_to_str(mrb, mrb_fixnum_value(int3), 10); 1420 | CREATE_REDIS_COMMAND_ARG3(argv, lens, cmd, str1, str2, str3); 1421 | return 4; 1422 | } 1423 | static inline int mrb_redis_create_command_str_int_int(mrb_state *mrb, const char *cmd, const char **argv, size_t *lens) 1424 | { 1425 | mrb_value str1, str2, str3; 1426 | mrb_int int2, int3; 1427 | mrb_get_args(mrb, "Sii", &str1, &int2, &int3); 1428 | str2 = mrb_fixnum_to_str(mrb, mrb_fixnum_value(int2), 10); 1429 | str3 = mrb_fixnum_to_str(mrb, mrb_fixnum_value(int3), 10); 1430 | CREATE_REDIS_COMMAND_ARG3(argv, lens, cmd, str1, str2, str3); 1431 | return 4; 1432 | } 1433 | static inline int mrb_redis_create_command_str_float_str(mrb_state *mrb, const char *cmd, const char **argv, 1434 | size_t *lens) 1435 | { 1436 | mrb_value str1, str2, str3; 1437 | mrb_float float2; 1438 | mrb_get_args(mrb, "SfS", &str1, &float2, &str3); 1439 | str2 = mrb_float_to_str(mrb, mrb_float_value(mrb, float2), "%g"); 1440 | CREATE_REDIS_COMMAND_ARG3(argv, lens, cmd, str1, str2, str3); 1441 | return 4; 1442 | } 1443 | 1444 | static inline redisContext *mrb_redis_get_context(mrb_state *mrb, mrb_value self) 1445 | { 1446 | redisContext *context = DATA_PTR(self); 1447 | if (!context) { 1448 | mrb_raise(mrb, E_REDIS_ERR_CLOSED, "connection is already closed or not initialized yet."); 1449 | } 1450 | return context; 1451 | } 1452 | 1453 | static inline mrb_value mrb_redis_execute_command(mrb_state *mrb, mrb_value self, int argc, const char **argv, 1454 | const size_t *lens, const ReplyHandlingRule *rule) 1455 | { 1456 | mrb_value ret; 1457 | redisReply *rr; 1458 | redisContext *rc = mrb_redis_get_context(mrb, self); 1459 | 1460 | rr = redisCommandArgv(rc, argc, argv, lens); 1461 | if (rc->err) { 1462 | mrb_redis_check_error(rc, mrb); 1463 | } 1464 | 1465 | ret = mrb_redis_get_reply(rr, mrb, rule); 1466 | freeReplyObject(rr); 1467 | return ret; 1468 | } 1469 | 1470 | void mrb_mruby_redis_gem_init(mrb_state *mrb) 1471 | { 1472 | struct RClass *redis; 1473 | 1474 | redis = mrb_define_class(mrb, "Redis", mrb->object_class); 1475 | MRB_SET_INSTANCE_TT(redis, MRB_TT_DATA); 1476 | 1477 | mrb_define_class_under(mrb, redis, "ConnectionError", E_RUNTIME_ERROR); 1478 | mrb_define_class_under(mrb, redis, "ConnectionError", E_RUNTIME_ERROR); 1479 | mrb_define_class_under(mrb, redis, "ReplyError", E_RUNTIME_ERROR); 1480 | mrb_define_class_under(mrb, redis, "ProtocolError", E_RUNTIME_ERROR); 1481 | mrb_define_class_under(mrb, redis, "OOMError", E_RUNTIME_ERROR); 1482 | mrb_define_class_under(mrb, redis, "AuthError", E_RUNTIME_ERROR); 1483 | mrb_define_class_under(mrb, redis, "ClosedError", E_RUNTIME_ERROR); 1484 | 1485 | mrb_define_method(mrb, redis, "initialize", mrb_redis_connect, MRB_ARGS_ANY()); 1486 | 1487 | /* use mruby-pointer for sharing between mrb_states */ 1488 | mrb_define_class_method(mrb, redis, "connect_set_raw", mrb_redis_connect_set_raw, MRB_ARGS_ANY()); 1489 | mrb_define_class_method(mrb, redis, "connect_set_udptr", mrb_redis_connect_set_raw, MRB_ARGS_ANY()); 1490 | 1491 | mrb_define_method(mrb, redis, "enable_keepalive", mrb_redis_enable_keepalive, MRB_ARGS_NONE()); 1492 | mrb_define_method(mrb, redis, "keepalive", mrb_redis_keepalive, MRB_ARGS_NONE()); 1493 | mrb_define_method(mrb, redis, "auth", mrb_redis_auth, MRB_ARGS_REQ(1)); 1494 | mrb_define_method(mrb, redis, "select", mrb_redis_select, MRB_ARGS_REQ(1)); 1495 | mrb_define_method(mrb, redis, "ping", mrb_redis_ping, MRB_ARGS_NONE()); 1496 | mrb_define_method(mrb, redis, "host", mrb_redis_host, MRB_ARGS_NONE()); 1497 | mrb_define_method(mrb, redis, "port", mrb_redis_port, MRB_ARGS_NONE()); 1498 | mrb_define_method(mrb, redis, "set", mrb_redis_set, MRB_ARGS_ARG(2, 1)); 1499 | mrb_define_method(mrb, redis, "get", mrb_redis_get, MRB_ARGS_ANY()); 1500 | mrb_define_method(mrb, redis, "exists?", mrb_redis_exists, MRB_ARGS_REQ(1)); 1501 | mrb_define_method(mrb, redis, "expire", mrb_redis_expire, MRB_ARGS_REQ(2)); 1502 | mrb_define_method(mrb, redis, "flushall", mrb_redis_flushall, MRB_ARGS_NONE()); 1503 | mrb_define_method(mrb, redis, "flushdb", mrb_redis_flushdb, MRB_ARGS_NONE()); 1504 | mrb_define_method(mrb, redis, "randomkey", mrb_redis_randomkey, MRB_ARGS_NONE()); 1505 | mrb_define_method(mrb, redis, "[]=", mrb_redis_set, MRB_ARGS_ANY()); 1506 | mrb_define_method(mrb, redis, "[]", mrb_redis_get, MRB_ARGS_ANY()); 1507 | mrb_define_method(mrb, redis, "keys", mrb_redis_keys, MRB_ARGS_REQ(1)); 1508 | mrb_define_method(mrb, redis, "del", mrb_redis_del, MRB_ARGS_ANY()); 1509 | mrb_define_method(mrb, redis, "incr", mrb_redis_incr, MRB_ARGS_OPT(1)); 1510 | mrb_define_method(mrb, redis, "decr", mrb_redis_decr, MRB_ARGS_OPT(1)); 1511 | mrb_define_method(mrb, redis, "incrby", mrb_redis_incrby, MRB_ARGS_REQ(2)); 1512 | mrb_define_method(mrb, redis, "decrby", mrb_redis_decrby, MRB_ARGS_REQ(2)); 1513 | mrb_define_method(mrb, redis, "llen", mrb_redis_llen, MRB_ARGS_REQ(1)); 1514 | mrb_define_method(mrb, redis, "rpush", mrb_redis_rpush, MRB_ARGS_OPT(2)); 1515 | mrb_define_method(mrb, redis, "lpush", mrb_redis_lpush, MRB_ARGS_OPT(2)); 1516 | mrb_define_method(mrb, redis, "rpop", mrb_redis_rpop, MRB_ARGS_REQ(1)); 1517 | mrb_define_method(mrb, redis, "lpop", mrb_redis_lpop, MRB_ARGS_REQ(1)); 1518 | mrb_define_method(mrb, redis, "lrange", mrb_redis_lrange, MRB_ARGS_ANY()); 1519 | mrb_define_method(mrb, redis, "ltrim", mrb_redis_ltrim, MRB_ARGS_ANY()); 1520 | mrb_define_method(mrb, redis, "lindex", mrb_redis_lindex, MRB_ARGS_REQ(2)); 1521 | mrb_define_method(mrb, redis, "mset", mrb_redis_mset, MRB_ARGS_ANY()); 1522 | mrb_define_method(mrb, redis, "mget", mrb_redis_mget, MRB_ARGS_ANY()); 1523 | mrb_define_method(mrb, redis, "sadd", mrb_redis_sadd, MRB_ARGS_ANY()); 1524 | mrb_define_method(mrb, redis, "srem", mrb_redis_srem, MRB_ARGS_ANY()); 1525 | mrb_define_method(mrb, redis, "sismember", mrb_redis_sismember, MRB_ARGS_REQ(2)); 1526 | mrb_define_method(mrb, redis, "smembers", mrb_redis_smembers, MRB_ARGS_REQ(1)); 1527 | mrb_define_method(mrb, redis, "scard", mrb_redis_scard, MRB_ARGS_REQ(1)); 1528 | mrb_define_method(mrb, redis, "spop", mrb_redis_spop, MRB_ARGS_REQ(1)); 1529 | mrb_define_method(mrb, redis, "hset", mrb_redis_hset, MRB_ARGS_REQ(3)); 1530 | mrb_define_method(mrb, redis, "hsetnx", mrb_redis_hsetnx, MRB_ARGS_REQ(3)); 1531 | mrb_define_method(mrb, redis, "hget", mrb_redis_hget, MRB_ARGS_REQ(2)); 1532 | mrb_define_method(mrb, redis, "hgetall", mrb_redis_hgetall, MRB_ARGS_REQ(1)); 1533 | mrb_define_method(mrb, redis, "hdel", mrb_redis_hdel, MRB_ARGS_REQ(2)); 1534 | mrb_define_method(mrb, redis, "hexists?", mrb_redis_hexists, MRB_ARGS_REQ(2)); 1535 | mrb_define_method(mrb, redis, "hkeys", mrb_redis_hkeys, MRB_ARGS_REQ(1)); 1536 | mrb_define_method(mrb, redis, "hmset", mrb_redis_hmset, (MRB_ARGS_REQ(3) | MRB_ARGS_REST())); 1537 | mrb_define_method(mrb, redis, "hmget", mrb_redis_hmget, (MRB_ARGS_REQ(2) | MRB_ARGS_REST())); 1538 | mrb_define_method(mrb, redis, "hvals", mrb_redis_hvals, MRB_ARGS_REQ(1)); 1539 | mrb_define_method(mrb, redis, "hincrby", mrb_redis_hincrby, MRB_ARGS_REQ(3)); 1540 | mrb_define_method(mrb, redis, "ttl", mrb_redis_ttl, MRB_ARGS_REQ(2)); 1541 | mrb_define_method(mrb, redis, "zadd", mrb_redis_zadd, MRB_ARGS_REQ(3)); 1542 | mrb_define_method(mrb, redis, "zcard", mrb_redis_zcard, MRB_ARGS_REQ(1)); 1543 | mrb_define_method(mrb, redis, "zrange", mrb_redis_zrange, MRB_ARGS_REQ(3)); 1544 | mrb_define_method(mrb, redis, "zrevrange", mrb_redis_zrevrange, MRB_ARGS_REQ(3)); 1545 | mrb_define_method(mrb, redis, "zrank", mrb_redis_zrank, MRB_ARGS_REQ(2)); 1546 | mrb_define_method(mrb, redis, "zrevrank", mrb_redis_zrevrank, MRB_ARGS_REQ(2)); 1547 | mrb_define_method(mrb, redis, "zscore", mrb_redis_zscore, MRB_ARGS_REQ(2)); 1548 | mrb_define_method(mrb, redis, "pfadd", mrb_redis_pfadd, (MRB_ARGS_REQ(1) | MRB_ARGS_REST())); 1549 | mrb_define_method(mrb, redis, "pfcount", mrb_redis_pfcount, (MRB_ARGS_REQ(1) | MRB_ARGS_REST())); 1550 | mrb_define_method(mrb, redis, "pfmerge", mrb_redis_pfmerge, (MRB_ARGS_REQ(2) | MRB_ARGS_REST())); 1551 | mrb_define_method(mrb, redis, "publish", mrb_redis_pub, MRB_ARGS_ANY()); 1552 | mrb_define_method(mrb, redis, "close", mrb_redis_close, MRB_ARGS_NONE()); 1553 | mrb_define_method(mrb, redis, "queue", mrb_redisAppendCommandArgv, (MRB_ARGS_REQ(1) | MRB_ARGS_REST())); 1554 | mrb_define_method(mrb, redis, "reply", mrb_redisGetReply, MRB_ARGS_NONE()); 1555 | mrb_define_method(mrb, redis, "bulk_reply", mrb_redisGetBulkReply, MRB_ARGS_NONE()); 1556 | mrb_define_method(mrb, redis, "multi", mrb_redis_multi, MRB_ARGS_NONE()); 1557 | mrb_define_method(mrb, redis, "exec", mrb_redis_exec, MRB_ARGS_NONE()); 1558 | mrb_define_method(mrb, redis, "discard", mrb_redis_discard, MRB_ARGS_NONE()); 1559 | mrb_define_method(mrb, redis, "watch", mrb_redis_watch, (MRB_ARGS_REQ(1) | MRB_ARGS_REST())); 1560 | mrb_define_method(mrb, redis, "unwatch", mrb_redis_unwatch, MRB_ARGS_NONE()); 1561 | mrb_define_method(mrb, redis, "setnx", mrb_redis_setnx, MRB_ARGS_REQ(2)); 1562 | mrb_define_method(mrb, redis, "cluster", mrb_redis_cluster, MRB_ARGS_REQ(1)); 1563 | mrb_define_method(mrb, redis, "asking", mrb_redis_asking, MRB_ARGS_NONE()); 1564 | DONE; 1565 | } 1566 | 1567 | void mrb_mruby_redis_gem_final(mrb_state *mrb) 1568 | { 1569 | } 1570 | 1571 | //#endif 1572 | -------------------------------------------------------------------------------- /src/mrb_redis.h: -------------------------------------------------------------------------------- 1 | /* 2 | // mrb_redis.h - to provide redis methods 3 | // 4 | // See Copyright Notice in mrb_redis.c 5 | */ 6 | 7 | #ifndef MRB_REDIS_H 8 | #define MRB_REDIS_H 9 | 10 | #include "mruby.h" 11 | 12 | void mrb_mruby_redis_gem_init(mrb_state *mrb); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /test/redis.rb: -------------------------------------------------------------------------------- 1 | ## 2 | ## Redis Test 3 | ## 4 | 5 | HOST = "127.0.0.1" 6 | PORT = 6379 7 | SECURED_PORT = 6380 8 | CLUSTER_PORT = 7000 9 | NUM_OF_CLUSTER_NODES = 6 10 | 11 | assert("Redis#ping") do 12 | r = Redis.new HOST, PORT 13 | 14 | assert_equal "PONG", r.ping 15 | r.close 16 | assert_raise(Redis::ClosedError) {r.ping} 17 | end 18 | 19 | assert("Non-authrozied Redis#ping") do 20 | r = Redis.new HOST, SECURED_PORT 21 | 22 | assert_raise(Redis::ReplyError) {r.ping} 23 | r.close 24 | end 25 | 26 | assert("Redis#enable_keepalive, Redis#keepalive") do 27 | r = Redis.new HOST, PORT 28 | 29 | assert_equal :off, r.keepalive 30 | assert_nil r.enable_keepalive 31 | assert_equal :on, r.keepalive 32 | 33 | r.close 34 | assert_raise(Redis::ClosedError) {r.keepalive} 35 | assert_raise(Redis::ClosedError) {r.enable_keepalive} 36 | end 37 | 38 | assert("Redis#host, Redis#port") do 39 | r = Redis.new HOST, PORT 40 | 41 | assert_equal HOST, r.host 42 | assert_equal PORT, r.port 43 | 44 | r.close 45 | 46 | assert_raise(Redis::ClosedError) {r.host} 47 | assert_raise(Redis::ClosedError) {r.port} 48 | end 49 | 50 | assert("Redis#select") do 51 | r = Redis.new HOST, PORT 52 | ret1 = r.select 0 53 | r.set "score", "0" 54 | db0_score = r.get("score") 55 | 56 | ret2 = r.select 1 57 | r.set "score", "1" 58 | db1_score = r.get("score") 59 | 60 | ret3 = r.select 0 61 | db0_1_score = r.get("score") 62 | 63 | assert_raise(Redis::ReplyError) {r.select(-1)} 64 | assert_raise(TypeError) {r.select nil} 65 | 66 | r.close 67 | 68 | assert_equal "OK", ret1 69 | assert_equal "0", db0_score 70 | assert_equal "OK", ret2 71 | assert_equal "1", db1_score 72 | assert_equal "OK", ret3 73 | assert_equal "0", db0_1_score 74 | assert_raise(Redis::ClosedError) {r.select 0} 75 | end 76 | 77 | assert("Redis#set, Redis#get") do 78 | r = Redis.new HOST, PORT 79 | result = r.set "hoge", "fuga" 80 | ret = r.get "hoge" 81 | r.close 82 | 83 | assert_equal "OK", result 84 | assert_equal "fuga", ret 85 | assert_raise(Redis::ClosedError) {r.get "hoge"} 86 | assert_raise(Redis::ClosedError) {r.set "hoge", "fuga"} 87 | end 88 | 89 | assert("Non-authrozied Redis#set, Redis#get") do 90 | r = Redis.new HOST, SECURED_PORT 91 | assert_raise(Redis::ReplyError) {r.get "hoge"} 92 | assert_raise(Redis::ReplyError) {r.set "hoge", "fuga"} 93 | r.close 94 | end 95 | 96 | assert("Redis#set, Redis#get for non-string") do 97 | r = Redis.new HOST, PORT 98 | assert_raise(TypeError) {r.set :hoge, 'bar'} 99 | assert_raise(TypeError) {r.set 'hoge', 10} 100 | assert_raise(TypeError) {r.get :hoge} 101 | r.close 102 | end 103 | 104 | assert("Redis#set with invalid args") do 105 | r = Redis.new HOST, PORT 106 | assert_raise(ArgumentError){r.set( "hoge", "fuga", {EX: "10", PX: "1"})} 107 | r.close 108 | end 109 | 110 | assert("Redis#set with PX as int") do 111 | r = Redis.new HOST, PORT 112 | r.set( "hoge", "fuga with PX as int", "PX" => 10_000) 113 | ret = r.get "hoge" 114 | ttl = r.ttl "hoge" 115 | r.close 116 | 117 | assert_equal "fuga with PX as int", ret 118 | assert_true 0 < ttl && ttl <= 10 119 | end 120 | 121 | assert("Redis#set with both NX and XX") do 122 | r = Redis.new HOST, PORT 123 | result1 = r.set( "hoge", "fuga") 124 | result2 = r.set( "hoge", "fuga with both NX and XX", "NX" => false, "XX" => true) 125 | result3 = r.set( "hoge", "fuga with both NX and XX 2", "NX" => true, "XX" => false) 126 | ret = r.get "hoge" 127 | r.close 128 | 129 | assert_equal "fuga with both NX and XX", ret 130 | assert_equal "OK", result1 131 | assert_equal "OK", result2 132 | assert_nil result3 133 | end 134 | 135 | assert("Redis#set with conflict opts") do 136 | r = Redis.new HOST, PORT 137 | assert_raise(ArgumentError){ r.set( "hoge", "fuga", "EX" => 1, "PX" => 100)} 138 | assert_raise(ArgumentError){ r.set( "hoge", "fuga", "NX" => true, "XX" => true)} 139 | r.close 140 | end 141 | 142 | assert("Redis#set with unknown keys") do 143 | r = Redis.new HOST, PORT 144 | assert_raise(ArgumentError){ r.set( "hoge", "fuga", :EX => 100)} 145 | r.close 146 | end 147 | 148 | assert("Redis#setnx, Redis#get") do 149 | r = Redis.new HOST, PORT 150 | r.flushall 151 | assert_true r.setnx( "hoge", "fuga") 152 | assert_false r.setnx( "hoge", "piyo") 153 | assert_raise(TypeError) {r.setnx(nil, "fuga")} 154 | assert_raise(TypeError) {r.setnx("hoge", nil)} 155 | ret = r.get "hoge" 156 | r.close 157 | 158 | assert_equal "fuga", ret 159 | assert_raise(Redis::ClosedError) {r.setnx "hoge", "fuga"} 160 | end 161 | 162 | assert("Non-authrozied Redis#setnx") do 163 | r = Redis.new HOST, SECURED_PORT 164 | assert_raise(Redis::ReplyError) {r.setnx "hoge", "fuga"} 165 | r.close 166 | end 167 | 168 | assert("Redis#[]=, Redis#[]") do 169 | r = Redis.new HOST, PORT 170 | r["hoge"] = "fuga" 171 | r["ho\0ge"] = "fu\0ga" 172 | ret1 = r["hoge"] 173 | ret2 = r["ho\0ge"] 174 | r.close 175 | 176 | assert_equal "fuga", ret1 177 | assert_equal "fu\0ga", ret2 178 | end 179 | 180 | assert("Redis#exist?") do 181 | r = Redis.new HOST, PORT 182 | r.del "hoge" 183 | r.del "fuga" 184 | r.del "pi\0yo" 185 | r["hoge"] = "aaa" 186 | r["pi\0yo"] = "bbb" 187 | ret1 = r.exists? "hoge" 188 | ret2 = r.exists? "fuga" 189 | ret3 = r.exists? "hoge\0" 190 | ret4 = r.exists? "piyo" 191 | ret5 = r.exists? "pi\0yo" 192 | assert_raise(TypeError) {r.exists? nil} 193 | r.close 194 | 195 | assert_true ret1 196 | assert_false ret2 197 | assert_false ret3 198 | assert_false ret4 199 | assert_true ret5 200 | assert_raise(Redis::ClosedError) {r.exists? "hoge"} 201 | end 202 | 203 | assert("Non-authrozied Redis#exists?") do 204 | r = Redis.new HOST, SECURED_PORT 205 | assert_raise(Redis::ReplyError) {r.exists? "hoge"} 206 | r.close 207 | end 208 | 209 | assert("Redis#expire") do 210 | r = Redis.new HOST, PORT 211 | r.del "hoge" 212 | 213 | expire = r.expire "hoge", 1 214 | r.set "hoge", "a" 215 | expire2 = r.expire "hoge", 1 216 | ret = r.get "hoge" 217 | 218 | # 1000_000 micro sec is sensitive time 219 | # so 1100_000 micro sec is sufficient time. 220 | usleep 1100_000 221 | 222 | ret2 = r.get "hoge" 223 | 224 | assert_raise(TypeError) {r.expire nil, 1} 225 | assert_raise(TypeError) {r.expire "hoge", nil} 226 | 227 | r.close 228 | 229 | assert_false expire 230 | assert_true expire2 231 | assert_equal "a", ret 232 | assert_nil ret2 233 | assert_raise(Redis::ClosedError) {r.expire "hoge", 1} 234 | end 235 | 236 | assert("Non-authrozied Redis#expire") do 237 | r = Redis.new HOST, SECURED_PORT 238 | assert_raise(Redis::ReplyError) {r.expire "hoge", 1} 239 | r.close 240 | end 241 | 242 | assert("Redis#flushall") do 243 | r = Redis.new HOST, PORT 244 | r.set "key1", "a" 245 | r.set "key2", "b" 246 | ret1 = r.exists? "key1" 247 | ret2 = r.exists? "key2" 248 | result = r.flushall 249 | ret_after1 = r.exists? "key1" 250 | ret_after2 = r.exists? "key2" 251 | r.close 252 | 253 | assert_true ret1 254 | assert_true ret2 255 | assert_false ret_after1 256 | assert_false ret_after2 257 | assert_equal "OK", result 258 | assert_raise(Redis::ClosedError) {r.flushall} 259 | end 260 | 261 | assert("Non-authrozied Redis#flushall") do 262 | r = Redis.new HOST, SECURED_PORT 263 | assert_raise(Redis::ReplyError) {r.flushall} 264 | r.close 265 | end 266 | 267 | assert("Redis#flushdb") do 268 | r = Redis.new HOST, PORT 269 | r.set "key1", "a" 270 | r.set "key2", "b" 271 | ret1 = r.exists? "key1" 272 | ret2 = r.exists? "key2" 273 | result = r.flushdb 274 | ret_after1 = r.exists? "key1" 275 | ret_after2 = r.exists? "key2" 276 | r.close 277 | 278 | assert_true ret1 279 | assert_true ret2 280 | assert_false ret_after1 281 | assert_false ret_after2 282 | assert_equal "OK", result 283 | assert_raise(Redis::ClosedError) {r.flushdb} 284 | end 285 | 286 | assert("Non-authrozied Redis#flushdb") do 287 | r = Redis.new HOST, SECURED_PORT 288 | assert_raise(Redis::ReplyError) {r.flushdb} 289 | r.close 290 | end 291 | 292 | assert("Redis#del") do 293 | r = Redis.new HOST, PORT 294 | r.set "hoge", "a" 295 | ret = r.exists? "hoge" 296 | del1 = r.del "hoge" 297 | ret2 = r.exists? "hoge" 298 | del2 = r.del "hoge" 299 | 300 | r.set "fu\0ga", "b" 301 | ret3 = r.exists? "fu\0ga" 302 | del3 = r.del "fu\0ga" 303 | ret4 = r.exists? "fu\0ga" 304 | 305 | assert_raise(TypeError) {r.del nil} 306 | r.close 307 | 308 | assert_true ret 309 | assert_equal 1, del1 310 | assert_false ret2 311 | assert_equal 0, del2 312 | assert_true ret3 313 | assert_equal 1, del3 314 | assert_false ret4 315 | assert_raise(Redis::ClosedError) {r.del "hoge"} 316 | end 317 | 318 | assert("Non-authrozied Redis#del") do 319 | r = Redis.new HOST, SECURED_PORT 320 | assert_raise(Redis::ReplyError) {r.del "hoge"} 321 | r.close 322 | end 323 | 324 | assert("Redis#hset", "Redis#hget") do 325 | r = Redis.new HOST, PORT 326 | r.del "myhash" 327 | r.del "myhash\0" 328 | 329 | ret_set_f1_a = r.hset "myhash", "field1", "a" 330 | ret_get_f1_a = r.hget "myhash", "field1" 331 | ret_set_f1_b = r.hset "myhash", "field1", "b" 332 | ret_get_f1_b = r.hget "myhash", "field1" 333 | 334 | r.hset "myhash", "field2", "c" 335 | ret_get_f2 = r.hget "myhash", "field2" 336 | 337 | ret_set_f3_d = r.hset "myhash\0", "field3\0", "d\0" 338 | ret_get_f3_d = r.hget "myhash\0", "field3\0" 339 | ret_set_f3_e = r.hset "myhash\0", "field3\0", "e\0" 340 | ret_get_f3_e = r.hget "myhash\0", "field3\0" 341 | 342 | r.hset "myhash\0", "field4\0", "f\0" 343 | ret_get_f4 = r.hget "myhash\0", "field4\0" 344 | 345 | assert_raise(TypeError) {r.hset nil, "field1", "a"} 346 | assert_raise(TypeError) {r.hset "myhash", nil, "a"} 347 | assert_raise(TypeError) {r.hset "myhash", "field1", nil} 348 | assert_raise(TypeError) {r.hget nil, "field1"} 349 | assert_raise(TypeError) {r.hget "myhash", nil} 350 | 351 | r.close 352 | 353 | assert_true ret_set_f1_a # true if field is a new field in the hash and value was set. 354 | assert_false ret_set_f1_b # false if field already exists in the hash and the value was updated. 355 | assert_true ret_set_f3_d 356 | assert_false ret_set_f3_e 357 | 358 | assert_equal "a", ret_get_f1_a 359 | assert_equal "b", ret_get_f1_b 360 | 361 | assert_equal "c", ret_get_f2 362 | 363 | assert_equal "d\0", ret_get_f3_d 364 | assert_equal "e\0", ret_get_f3_e 365 | 366 | assert_equal "f\0", ret_get_f4 367 | 368 | assert_raise(Redis::ClosedError) {r.hset "myhash", "field1", "a"} 369 | assert_raise(Redis::ClosedError) {r.hget "myhash", "field1"} 370 | end 371 | 372 | assert("Non-authrozied Redis#hset, Redis#hget") do 373 | r = Redis.new HOST, SECURED_PORT 374 | assert_raise(Redis::ReplyError) {r.hset "myhash", "field1", "a"} 375 | assert_raise(Redis::ReplyError) {r.hget "myhash", "field1"} 376 | r.close 377 | end 378 | 379 | assert("Redis#hsetnx") do 380 | r = Redis.new HOST, PORT 381 | r.del "myhash" 382 | 383 | ret1 = r.hsetnx "myhash", "field1", "a" 384 | ret2 = r.hget "myhash", "field1" 385 | ret3 = r.hsetnx "myhash", "field1", "b" 386 | ret4 = r.hget "myhash", "field1" 387 | 388 | assert_raise(TypeError) {r.hsetnx nil, "field1", "a"} 389 | assert_raise(TypeError) {r.hsetnx "myhash", nil, "a"} 390 | assert_raise(TypeError) {r.hsetnx "myhash", "field1", nil} 391 | 392 | assert_true ret1 393 | assert_equal "a", ret2 394 | assert_false ret3 395 | assert_equal "a", ret4 396 | 397 | r.close 398 | assert_raise(Redis::ClosedError) {r.hsetnx "myhash", "field1", "a"} 399 | end 400 | 401 | assert("Non-authrozied Redis#hsetnx") do 402 | r = Redis.new HOST, SECURED_PORT 403 | assert_raise(Redis::ReplyError) {r.hsetnx "myhash", "field1", "a"} 404 | r.close 405 | end 406 | 407 | assert("Redis#hgetall") do 408 | r = Redis.new HOST, PORT 409 | r.del "myhash" 410 | 411 | hash = r.hgetall "myhash" 412 | 413 | r.hset "myhash", "field1", "a" 414 | r.hset "myhash", "field2", "b" 415 | r.hset "myhash", "field3\0", "c" 416 | r.hset "myhash", "field4", "d\0" 417 | 418 | hash2 = r.hgetall "myhash" 419 | 420 | assert_raise(TypeError) {r.hgetall nil} 421 | 422 | r.close 423 | 424 | assert_nil hash 425 | assert_equal({"field1"=>"a", "field2"=>"b","field3\0"=>"c", "field4"=>"d\0"}, hash2) 426 | assert_raise(Redis::ClosedError) {r.hgetall "myhash"} 427 | end 428 | 429 | assert("Non-authrozied Redis#hgetall") do 430 | r = Redis.new HOST, SECURED_PORT 431 | assert_raise(Redis::ReplyError) {r.hgetall "myhash"} 432 | r.close 433 | end 434 | 435 | assert("Redis#hdel") do 436 | r = Redis.new HOST, PORT 437 | r.del "myhash" 438 | r.del "myhash\0" 439 | 440 | r.hset "myhash", "field1", "a" 441 | r.hset "myhash", "field2", "b" 442 | r.hset "myhash\0", "field3\0", "c" 443 | r.hset "myhash\0", "field4\0", "d" 444 | ret_get_f1_a = r.hget "myhash", "field1" 445 | ret_get_f3_c = r.hget "myhash\0", "field3\0" 446 | ret_del_f1_1 = r.hdel "myhash", "field1" 447 | ret_del_f3_1 = r.hdel "myhash\0", "field3\0" 448 | ret_get_f1_nil = r.hget "myhash", "field1" 449 | ret_get_f3_nil = r.hget "myhash", "field3\0" 450 | ret_exists = r.exists?("myhash") 451 | ret_exists2 = r.exists?("myhash\0") 452 | 453 | assert_raise(TypeError) {r.hdel nil, "field1"} 454 | assert_raise(TypeError) {r.hdel "myhash", nil} 455 | 456 | r.close 457 | 458 | assert_equal "a", ret_get_f1_a 459 | assert_equal "c", ret_get_f3_c 460 | assert_equal 1, ret_del_f1_1 461 | assert_equal 1, ret_del_f3_1 462 | assert_nil ret_get_f1_nil 463 | assert_nil ret_get_f3_nil 464 | assert_true ret_exists 465 | assert_true ret_exists2 466 | assert_raise(Redis::ClosedError) {r.hdel "myhash", "field1"} 467 | end 468 | 469 | assert("Non-authrozied Redis#hdel") do 470 | r = Redis.new HOST, SECURED_PORT 471 | assert_raise(Redis::ReplyError) {r.hdel "myhash", "field1"} 472 | r.close 473 | end 474 | 475 | assert("Redis#hexists?") do 476 | r = Redis.new HOST, PORT 477 | r.del "myhash" 478 | r.del "myhash\0" 479 | 480 | r.hset "myhash", "field", "a" 481 | r.hset "myhash\0", "field\0", "b\0" 482 | ret_hexists = r.hexists?("myhash", "field") 483 | ret_hexists2 = r.hexists?("myhash", "invalid_field") 484 | ret_hexists3 = r.hexists?("myhash\0", "field\0") 485 | ret_hexists4 = r.hexists?("myhash\0", "invalid_field") 486 | 487 | assert_raise(TypeError) {r.hexists?(nil, "field")} 488 | assert_raise(TypeError) {r.hexists?("myhash", nil)} 489 | 490 | r.close 491 | 492 | assert_true ret_hexists 493 | assert_false ret_hexists2 494 | assert_true ret_hexists3 495 | assert_false ret_hexists4 496 | assert_raise(Redis::ClosedError) {r.hexists?("myhash", "field")} 497 | end 498 | 499 | assert("Non-authrozied Redis#hexists?") do 500 | r = Redis.new HOST, SECURED_PORT 501 | assert_raise(Redis::ReplyError) {r.hexists?("myhash", "field")} 502 | r.close 503 | end 504 | 505 | assert("Redis#hkeys") do 506 | r = Redis.new HOST, PORT 507 | r.del "myhash" 508 | 509 | keys = r.hkeys "myhash" 510 | 511 | r.hset "myhash", "field1", "a" 512 | r.hset "myhash", "field2", "b" 513 | r.hset "myhash", "field3\0", "c" 514 | r.hset "myhash", "field4", "d\0" 515 | 516 | keys2 = r.hkeys "myhash" 517 | 518 | assert_raise(TypeError) {r.hkeys nil} 519 | 520 | r.close 521 | 522 | assert_nil keys 523 | assert_equal ["field1", "field2", "field3\0", "field4"], keys2 524 | assert_raise(Redis::ClosedError) {r.hkeys "myhash"} 525 | end 526 | 527 | assert("Non-authrozied Redis#hkeys") do 528 | r = Redis.new HOST, SECURED_PORT 529 | assert_raise(Redis::ReplyError) {r.hkeys "myhash"} 530 | r.close 531 | end 532 | 533 | assert("Redis#hmset, Redis#hmget") do 534 | r = Redis.new HOST, PORT 535 | r.del "myhash" 536 | r.del "myhash\0" 537 | 538 | r.hmset "myhash", "field1", "a", "field2", "b" 539 | r.hmset "myhash", "field2", "c" 540 | ret_get_f12_ab = r.hmget "myhash", "field1", "field2" 541 | 542 | r.hmset "myhash\0", "field3\0", "c\0", "field4\0", "d\0" 543 | ret_get_f34_cd = r.hmget "myhash\0", "field3\0", "field4\0" 544 | 545 | ret_get_f12n_abn = r.hmget "myhash", "field1", "field2", "nofield" 546 | 547 | assert_raise(ArgumentError) {r.hmget "myhash"} 548 | assert_raise(ArgumentError) {r.hmset "myhash"} 549 | assert_raise(ArgumentError) {r.hmset "myhash", "field1"} 550 | assert_raise(ArgumentError) {r.hmset "myhash", "field1", "a", "field2"} 551 | 552 | r.close 553 | 554 | assert_equal ["a", "c"], ret_get_f12_ab 555 | assert_equal ["c\0", "d\0"], ret_get_f34_cd 556 | assert_equal ["a", "c", nil], ret_get_f12n_abn 557 | assert_raise(Redis::ClosedError) {r.hmset "myhash", "field1", "a"} 558 | assert_raise(Redis::ClosedError) {r.hmget "myhash", "field1"} 559 | end 560 | 561 | assert("Redis#hvals") do 562 | r = Redis.new HOST, PORT 563 | r.del "myhash" 564 | 565 | vals = r.hvals "myhash" 566 | 567 | r.hset "myhash", "field1", "a" 568 | r.hset "myhash", "field2", "b" 569 | r.hset "myhash", "field3\0", "c" 570 | r.hset "myhash", "field4", "d\0" 571 | 572 | vals2 = r.hvals "myhash" 573 | 574 | assert_raise(TypeError) {r.hvals nil} 575 | 576 | r.close 577 | 578 | assert_nil vals 579 | assert_equal ["a", "b", "c", "d\0"], vals2 580 | assert_raise(Redis::ClosedError) {r.hvals "myhash"} 581 | end 582 | 583 | assert("Non-authrozied Redis#hvals") do 584 | r = Redis.new HOST, SECURED_PORT 585 | assert_raise(Redis::ReplyError) {r.hvals "myhash"} 586 | r.close 587 | end 588 | 589 | assert("Redis#hincrby") do 590 | r = Redis.new HOST, PORT 591 | r.del "myhash" 592 | 593 | r.hset "myhash", "score", "10" 594 | r.hset "myhash", "score\0", "20" 595 | ret = r.hincrby "myhash", "score", 100 596 | ret2 = r.hincrby "myhash", "score\0", 200 597 | score = r.hget "myhash", "score" 598 | score2 = r.hget "myhash", "score\0" 599 | 600 | assert_raise(TypeError) {r.hincrby nil, "score", 100} 601 | assert_raise(TypeError) {r.hincrby "myhash", nil, 100} 602 | assert_raise(TypeError) {r.hincrby "myhash", "score", nil} 603 | 604 | r.close 605 | 606 | assert_equal 110, ret 607 | assert_equal 220, ret2 608 | assert_equal "110", score 609 | assert_equal "220", score2 610 | assert_raise(Redis::ClosedError) {r.hincrby "myhash", "score", 100} 611 | end 612 | 613 | assert("Non-authrozied Redis#hincrby") do 614 | r = Redis.new HOST, SECURED_PORT 615 | assert_raise(Redis::ReplyError) {r.hincrby "myhash", "score", 100} 616 | r.close 617 | end 618 | 619 | assert("Redis#incrby") do 620 | r = Redis.new HOST, PORT 621 | r.del "score" 622 | r.del "score\0" 623 | 624 | r.set "score", "10" 625 | r.set "score\0", "20" 626 | ret = r.incrby "score", 100 627 | ret2 = r.incrby "score\0", 200 628 | score = r.get "score" 629 | score2 = r.get "score\0" 630 | 631 | assert_raise(TypeError) {r.incrby nil, 100} 632 | assert_raise(TypeError) {r.incrby "score", nil} 633 | 634 | r.close 635 | 636 | assert_equal 110, ret 637 | assert_equal 220, ret2 638 | assert_equal "110", score 639 | assert_equal "220", score2 640 | assert_raise(Redis::ClosedError) {r.incrby "score", 100} 641 | end 642 | 643 | assert("Non-authrozied Redis#incrby") do 644 | r = Redis.new HOST, SECURED_PORT 645 | assert_raise(Redis::ReplyError) {r.incrby "score", 100} 646 | r.close 647 | end 648 | 649 | assert("Redis#decrby") do 650 | r = Redis.new HOST, PORT 651 | r.del "score" 652 | r.del "score\0" 653 | 654 | r.set "score", "10" 655 | r.set "score\0", "20" 656 | ret = r.decrby "score", 100 657 | ret2 = r.decrby "score\0", 200 658 | score = r.get "score" 659 | score2 = r.get "score\0" 660 | 661 | assert_raise(TypeError) {r.decrby nil, 100} 662 | assert_raise(TypeError) {r.decrby "score", nil} 663 | 664 | r.close 665 | 666 | assert_equal(-90, ret) 667 | assert_equal(-180, ret2) 668 | assert_equal "-90", score 669 | assert_equal "-180", score2 670 | assert_raise(Redis::ClosedError) {r.decrby "score", 100} 671 | end 672 | 673 | assert("Non-authrozied Redis#decrby") do 674 | r = Redis.new HOST, SECURED_PORT 675 | assert_raise(Redis::ReplyError) {r.decrby "score", 100} 676 | r.close 677 | end 678 | 679 | assert("Redis#lpush", "Redis#lrange") do 680 | r = Redis.new HOST, PORT 681 | r.del "logs" 682 | 683 | range1 = r.lrange "logs", 0, -1 684 | ret1 = r.lpush "logs", "error1" 685 | ret2 = r.lpush "logs", "error2\0" 686 | ret3 = r.lpush "logs", "error3" 687 | range2 = r.lrange "logs", 0, 0 688 | range3 = r.lrange "logs", -3, 2 689 | range4 = r.lrange "logs", 0, -1 690 | range5 = r.lrange "logs", -100, 100 691 | range6 = r.lrange "logs", 5, 10 692 | 693 | assert_raise(TypeError) {r.lpush nil, "error1"} 694 | assert_raise(TypeError) {r.lpush "logs", nil} 695 | assert_raise(TypeError) {r.lrange nil, 0, -1} 696 | assert_raise(TypeError) {r.lrange "logs", nil, -1} 697 | assert_raise(TypeError) {r.lrange "logs", 0, nil} 698 | 699 | r.close 700 | 701 | assert_equal 1, ret1 702 | assert_equal 2, ret2 703 | assert_equal 3, ret3 704 | 705 | assert_equal [], range1 706 | assert_equal ["error3"], range2 707 | assert_equal ["error3", "error2\0", "error1"], range3 708 | assert_equal ["error3", "error2\0", "error1"], range4 709 | assert_equal ["error3", "error2\0", "error1"], range5 710 | assert_equal [], range6 711 | assert_raise(Redis::ClosedError) {r.lrange "logs", 0, -1} 712 | assert_raise(Redis::ClosedError) {r.lpush "logs", "error1"} 713 | end 714 | 715 | assert("Non-authrozied Redis#lpush, Redis#lrange") do 716 | r = Redis.new HOST, SECURED_PORT 717 | assert_raise(Redis::ReplyError) {r.lrange "logs", 0, -1} 718 | assert_raise(Redis::ReplyError) {r.lpush "logs", "error1"} 719 | r.close 720 | end 721 | 722 | assert("Redis#rpop") do 723 | r = Redis.new HOST, PORT 724 | r.del "list" 725 | 726 | r.rpush "list", "one" 727 | r.rpush "list", "two" 728 | r.rpush "list", "three\0" 729 | range1 = r.lrange "list", 0, -1 730 | ret = r.rpop "list" 731 | range2 = r.lrange "list", 0, -1 732 | 733 | assert_raise(TypeError) {r.rpop nil} 734 | 735 | r.close 736 | 737 | assert_equal ["one", "two", "three\0"], range1 738 | assert_equal "three\0", ret 739 | assert_equal ["one", "two"], range2 740 | assert_raise(Redis::ClosedError) {r.rpop "list"} 741 | end 742 | 743 | assert("Non-authrozied Redis#rpop") do 744 | r = Redis.new HOST, SECURED_PORT 745 | assert_raise(Redis::ReplyError) {r.rpop "list"} 746 | r.close 747 | end 748 | 749 | assert("Redis#lpop") do 750 | r = Redis.new HOST, PORT 751 | r.del "list" 752 | 753 | r.rpush "list", "one\0" 754 | r.rpush "list", "two" 755 | r.rpush "list", "three" 756 | range1 = r.lrange "list", 0, -1 757 | ret = r.lpop "list" 758 | range2 = r.lrange "list", 0, -1 759 | 760 | assert_raise(TypeError) {r.lpop nil} 761 | 762 | r.close 763 | 764 | assert_equal ["one\0", "two", "three"], range1 765 | assert_equal "one\0", ret 766 | assert_equal ["two", "three"], range2 767 | assert_raise(Redis::ClosedError) {r.lpop "list"} 768 | end 769 | 770 | assert("Non-authrozied Redis#lpop") do 771 | r = Redis.new HOST, SECURED_PORT 772 | assert_raise(Redis::ReplyError) {r.lpop "list"} 773 | r.close 774 | end 775 | 776 | assert('Redis#mget') do 777 | r = Redis.new HOST, PORT 778 | 779 | r.set "foo", "hoge" 780 | r.set "bar", "fuga" 781 | ret1 = r.mget("foo", "bar") 782 | r.flushall 783 | 784 | r.set "one", "1" 785 | ret2 = r.mget("one", "two") 786 | r.flushall 787 | 788 | ret3 = r.mget("piyo","puyo") 789 | r.close 790 | 791 | assert_equal ["hoge", "fuga"], ret1 792 | assert_equal ["1", nil], ret2 793 | assert_equal [nil, nil], ret3 794 | assert_raise(Redis::ClosedError) {r.mget("foo", "bar")} 795 | end 796 | 797 | assert('Redis#mset, Redis#mget') do 798 | r = Redis.new HOST, PORT 799 | 800 | r.mset "hoge", "one", "fuga", "two" 801 | ret = r.mget("hoge", "fuga", "piyo") 802 | r.flushall 803 | 804 | r.close 805 | 806 | assert_equal ["one", "two", nil ], ret 807 | assert_raise(Redis::ClosedError) {r.mset "hoge", "one", "fuga", "two"} 808 | end 809 | 810 | assert("Redis#sadd, Redis#sismember ") do 811 | r = Redis.new HOST, PORT 812 | assert_equal 1, r.sadd('set', 'test') 813 | r.spop 'set' 814 | 815 | assert_equal 1, r.sadd('set', 'bar') 816 | assert_equal 1, r.scard('set') 817 | 818 | assert_equal 1, r.sismember('set', 'bar') 819 | assert_equal 0, r.sismember('set', 'buzz') 820 | 821 | assert_equal 'bar', r.spop('set') 822 | 823 | assert_equal 2, r.sadd('set', 'bar', 'buzz') 824 | assert_equal 2, r.scard('set') 825 | 826 | assert_equal 1, r.sismember('set', 'bar') 827 | assert_equal 1, r.sismember('set', 'buzz') 828 | assert_equal 0, r.sismember('set', 'foo') 829 | 830 | assert_raise(TypeError) {r.sismember nil, 'bar'} 831 | assert_raise(TypeError) {r.sismember 'set', nil} 832 | 833 | assert_equal ['bar', 'buzz'], r.smembers('set').sort 834 | 835 | r.flushall 836 | r.close 837 | 838 | assert_raise(Redis::ClosedError) {r.sadd('set', 'bar')} 839 | assert_raise(Redis::ClosedError) {r.sismember('set', 'bar')} 840 | end 841 | 842 | assert("Non-authrozied Redis#sadd, Redis#sismember") do 843 | r = Redis.new HOST, SECURED_PORT 844 | assert_raise(Redis::ReplyError) {r.sadd('set', 'bar')} 845 | assert_raise(Redis::ReplyError) {r.sismember('set', 'bar')} 846 | r.close 847 | end 848 | 849 | assert("Redis#scard Redis#smembers Redis#spop") do 850 | r = Redis.new HOST, PORT 851 | 852 | assert_equal 0, r.scard('set') 853 | 854 | r.sadd 'set', 'bar' 855 | 856 | assert_equal 1, r.scard('set') 857 | 858 | assert_equal ['bar'], r.smembers('set') 859 | 860 | assert_equal 'bar', r.spop('set') 861 | 862 | assert_raise(TypeError) {r.scard nil} 863 | assert_raise(TypeError) {r.smembers nil} 864 | assert_raise(TypeError) {r.spop nil} 865 | 866 | r.close 867 | 868 | assert_raise(Redis::ClosedError) {r.scard('set')} 869 | assert_raise(Redis::ClosedError) {r.smembers('set')} 870 | assert_raise(Redis::ClosedError) {r.spop('set')} 871 | end 872 | 873 | assert("Non-authrozied Redis#scard Redis#smembers Redis#spop") do 874 | r = Redis.new HOST, SECURED_PORT 875 | assert_raise(Redis::ReplyError) {r.scard('set')} 876 | assert_raise(Redis::ReplyError) {r.smembers('set')} 877 | assert_raise(Redis::ReplyError) {r.spop('set')} 878 | r.close 879 | end 880 | 881 | assert("Redis#srem") do 882 | r = Redis.new HOST, PORT 883 | 884 | r.sadd 'set', 'hoge' 885 | 886 | assert_equal 1, r.scard('set') 887 | 888 | assert_equal 1, r.srem("set", "hoge") 889 | assert_equal 0, r.srem("set", "fuga") 890 | 891 | assert_equal 0, r.scard('set') 892 | 893 | r.sadd 'set', 'a', 'b', 'c' 894 | 895 | assert_equal 3, r.scard('set') 896 | 897 | assert_equal 2, r.srem('set', 'a', 'b') 898 | assert_equal 1, r.srem('set', 'c', 'd', 'e') 899 | assert_equal 0, r.srem('set', 'x', 'y', 'z') 900 | 901 | assert_equal 0, r.scard('set') 902 | 903 | r.close 904 | 905 | assert_raise(Redis::ClosedError) {r.srem("set", "hoge")} 906 | end 907 | 908 | assert("Non-authrozied Redis#srem") do 909 | r = Redis.new HOST, SECURED_PORT 910 | assert_raise(Redis::ReplyError) {r.srem("set", "hoge")} 911 | r.close 912 | end 913 | 914 | assert("Redis#ttl") do 915 | r = Redis.new HOST, PORT 916 | r.del "hoge" 917 | r.del "hoge\0" 918 | r.del "fuga" 919 | r.del "fuga\0" 920 | 921 | r.set "hoge", "a" 922 | r.set "hoge\0", "a" 923 | r.expire "hoge", 1 924 | r.expire "hoge\0", 1 925 | ttl = r.ttl "hoge" 926 | ttl4 = r.ttl "hoge\0" 927 | 928 | # 1_000_000 micro sec is sensitive time 929 | # so 1_100_000 micro sec is sufficient time. 930 | usleep 1_100_000 931 | 932 | ttl2 = r.ttl "hoge" 933 | ttl5 = r.ttl "hoge\0" 934 | r.set "fuga", "b" 935 | ttl3 = r.ttl "fuga" 936 | 937 | assert_raise(TypeError) {r.ttl nil} 938 | 939 | r.close 940 | 941 | assert_equal( 1, ttl) 942 | assert_equal(-2, ttl2) 943 | assert_equal(-1, ttl3) 944 | assert_equal( 1, ttl4) 945 | assert_equal(-2, ttl5) 946 | assert_raise(Redis::ClosedError) {r.ttl "hoge"} 947 | end 948 | 949 | assert("Non-authrozied Redis#ttl") do 950 | r = Redis.new HOST, SECURED_PORT 951 | assert_raise(Redis::ReplyError) {r.ttl "hoge"} 952 | r.close 953 | end 954 | 955 | assert("Redis#keys") do 956 | r = Redis.new HOST, PORT 957 | 958 | r.set "foo", "foo" 959 | r.set "bar", "bar" 960 | 961 | only_foo = r.keys 'fo*' 962 | only_bar = r.keys '*ar' 963 | 964 | assert_raise(TypeError) {r.keys nil} 965 | 966 | r.close 967 | 968 | assert_equal ['foo'], only_foo 969 | assert_equal ['bar'], only_bar 970 | assert_raise(Redis::ClosedError) {r.keys 'fo*'} 971 | end 972 | 973 | assert("Non-authrozied Redis#keys") do 974 | r = Redis.new HOST, SECURED_PORT 975 | assert_raise(Redis::ReplyError) {r.keys 'fo*'} 976 | r.close 977 | end 978 | 979 | assert("Redis#llen") do 980 | r = Redis.new HOST, PORT 981 | 982 | r.lpush('mylist', 'hello') 983 | r.lpush('mylist', 'world') 984 | len = r.llen('mylist') 985 | 986 | assert_raise(TypeError) {r.llen nil} 987 | 988 | r.close 989 | 990 | assert_equal 2, len 991 | assert_raise(Redis::ClosedError) {r.llen('mylist')} 992 | end 993 | 994 | assert("Non-authrozied Redis#llen") do 995 | r = Redis.new HOST, SECURED_PORT 996 | assert_raise(Redis::ReplyError) {r.llen('mylist')} 997 | r.close 998 | end 999 | 1000 | assert("Redis#lindex") do 1001 | r = Redis.new HOST, PORT 1002 | 1003 | r.lpush('mylist', 'hello') 1004 | r.lpush('mylist', 'world') 1005 | val = r.lindex('mylist', 2) 1006 | 1007 | assert_raise(TypeError) {r.lindex(nil, 2)} 1008 | assert_raise(TypeError) {r.lindex('mylist', nil)} 1009 | 1010 | r.close 1011 | 1012 | assert_equal 'world', val 1013 | assert_raise(Redis::ClosedError) {r.lindex('mylist', 2)} 1014 | end 1015 | 1016 | assert("Non-authrozied Redis#lindex") do 1017 | r = Redis.new HOST, SECURED_PORT 1018 | assert_raise(Redis::ReplyError) {r.lindex('mylist', 2)} 1019 | r.close 1020 | end 1021 | 1022 | assert("Redis#new") do 1023 | assert_raise(Redis::ConnectionError) { Redis.new "10.10.10.10", 6379 } 1024 | assert_raise(Redis::ConnectionError) { Redis.new "10.10.10.10", 6379, 0 } 1025 | assert_raise(Redis::ConnectionError) { Redis.new "10.10.10.10", 6379, 1 } 1026 | assert_raise(TypeError) { Redis.new "10.10.10.10", 6379, nil } 1027 | end 1028 | 1029 | # got erro for travis ci. comment out until fix the problems 1030 | #assert("Redis#zadd, Redis#zrange") do 1031 | # r = Redis.new HOST, PORT 1032 | # r.del "hs" 1033 | # r.zadd "hs", 80, "a" 1034 | # r.zadd "hs", 50.1, "b" 1035 | # r.zadd "hs", 60, "c" 1036 | # ret = r.zrange "hs", 0, -1 1037 | # r.close 1038 | # 1039 | # assert_equal ["b", "c", "a"], ret 1040 | #end 1041 | 1042 | assert("Redis#zcard") do 1043 | r = Redis.new HOST, PORT 1044 | r.zadd "myzset", 1, "one" 1045 | r.zadd "myzset", 2, "two" 1046 | 1047 | assert_equal 2, r.zcard("myzset") 1048 | assert_raise(TypeError) {r.zcard(nil)} 1049 | 1050 | r.close 1051 | 1052 | assert_raise(Redis::ClosedError) {r.zcard("myzset")} 1053 | end 1054 | 1055 | assert("Non-authrozied Redis#zcard") do 1056 | r = Redis.new HOST, SECURED_PORT 1057 | assert_raise(Redis::ReplyError) {r.zcard("myzset")} 1058 | r.close 1059 | end 1060 | 1061 | assert("Pipelined commands") do 1062 | redis = Redis.new HOST, PORT 1063 | redis.queue(:set, "mruby-redis-test:foo", "bar") 1064 | redis.queue(:get, "mruby-redis-test:foo") 1065 | assert_equal(:OK, redis.reply) 1066 | assert_equal("bar", redis.reply) 1067 | 1068 | redis.queue(:set, "mruby-redis-test:foo", "bar") 1069 | redis.queue(:get, "mruby-redis-test:foo") 1070 | assert_equal([:OK, "bar"], redis.bulk_reply) 1071 | 1072 | redis.queue(:nonexistant) 1073 | assert_kind_of(Redis::ReplyError, redis.reply) 1074 | 1075 | redis.queue(:set, "mruby-redis-test:foo", "bar") 1076 | redis.del("mruby-redis-test:foo") 1077 | 1078 | redis.close 1079 | 1080 | assert_raise(Redis::ClosedError) {redis.queue(:set, "mruby-redis-test:foo", "bar")} 1081 | assert_raise(Redis::ClosedError) {redis.bulk_reply} 1082 | assert_raise(Redis::ClosedError) {redis.reply} 1083 | end 1084 | 1085 | #assert("Redis#zrevrange") do 1086 | # r = Redis.new HOST, PORT 1087 | # r.del "hs" 1088 | # r.zadd "hs", 80, "a" 1089 | # r.zadd "hs", 50.1, "b" 1090 | # r.zadd "hs", 60, "c" 1091 | # ret = r.zrevrange "hs", 0, -1 1092 | # r.close 1093 | # 1094 | # assert_equal ["a", "c", "b"], ret 1095 | #end 1096 | # 1097 | #assert("Redis#zrank") do 1098 | # r = Redis.new HOST, PORT 1099 | # r.del "hs" 1100 | # r.zadd "hs", 80, "a" 1101 | # r.zadd "hs", 50.1, "b" 1102 | # r.zadd "hs", 60, "c" 1103 | # ret1 = r.zrank "hs", "b" 1104 | # ret2 = r.zrank "hs", "c" 1105 | # ret3 = r.zrank "hs", "a" 1106 | # r.close 1107 | # 1108 | # assert_equal 0, ret1 1109 | # assert_equal 1, ret2 1110 | # assert_equal 2, ret3 1111 | #end 1112 | # 1113 | #assert("Redis#zscore") do 1114 | # r = Redis.new HOST, PORT 1115 | # r.del "hs" 1116 | # r.zadd "hs", 80, "a" 1117 | # r.zadd "hs", 50.1, "b" 1118 | # r.zadd "hs", 60, "c" 1119 | # ret_a = r.zscore "hs", "a" 1120 | ## ret_b = r.zscore "hs", "b" 1121 | # ret_c = r.zscore "hs", "c" 1122 | # r.close 1123 | # 1124 | # assert_equal "80", ret_a 1125 | ## assert_equal "50.1", ret_b # value is not 50.1 in mruby and redis-cli 1126 | # assert_equal "60", ret_c 1127 | #end 1128 | 1129 | assert("Redis#randomkey") do 1130 | r = Redis.new HOST, PORT 1131 | r.flushdb 1132 | r.set "foo", "bar" 1133 | 1134 | assert_equal "foo", r.randomkey 1135 | 1136 | r.close 1137 | assert_raise(Redis::ClosedError) {r.randomkey} 1138 | end 1139 | 1140 | assert("Non-authrozied Redis#randomkey") do 1141 | r = Redis.new HOST, SECURED_PORT 1142 | assert_raise(Redis::ReplyError) {r.randomkey} 1143 | r.close 1144 | end 1145 | 1146 | assert("Redis#ltrim") do 1147 | r = Redis.new HOST, PORT 1148 | r.rpush "mylist", "one" 1149 | r.rpush "mylist", "two" 1150 | r.rpush "mylist", "three" 1151 | 1152 | r.ltrim "mylist", 1, -1 1153 | 1154 | assert_raise(TypeError) {r.ltrim nil, 1, -1} 1155 | assert_raise(TypeError) {r.ltrim "mylist", nil, -1} 1156 | assert_raise(TypeError) {r.ltrim "mylist", 1, nil} 1157 | 1158 | results = r.lrange "mylist", 0, -1 1159 | assert_equal ["two", "three"], results 1160 | 1161 | r.close 1162 | assert_raise(Redis::ClosedError) {r.ltrim "mylist", 1, -1} 1163 | end 1164 | 1165 | assert("Non-authrozied Redis#ltrim") do 1166 | r = Redis.new HOST, SECURED_PORT 1167 | assert_raise(Redis::ReplyError) {r.ltrim "mylist", 1, -1} 1168 | r.close 1169 | end 1170 | 1171 | assert("Redis#publish") do 1172 | producer = Redis.new HOST, PORT 1173 | 1174 | assert_equal 0, producer.publish("some_queue", "hello world") 1175 | 1176 | assert_raise(TypeError) {producer.publish(nil, "hello world")} 1177 | assert_raise(TypeError) {producer.publish("some_queue", nil)} 1178 | 1179 | producer.close 1180 | assert_raise(Redis::ClosedError) {producer.publish("some_queue", "hello world")} 1181 | end 1182 | 1183 | assert("Non-authrozied Redis#publish") do 1184 | r = Redis.new HOST, SECURED_PORT 1185 | assert_raise(Redis::ReplyError) {r.publish("some_queue", "hello world")} 1186 | r.close 1187 | end 1188 | 1189 | assert("Redis#pfadd") do 1190 | r = Redis.new HOST, PORT 1191 | assert_equal 1, r.pfadd("foos") 1192 | assert_equal 0, r.pfadd("foos") 1193 | assert_equal 1, r.pfadd("foos", "bar") 1194 | assert_equal 1, r.pfadd("foos", "baz") 1195 | assert_equal 0, r.pfadd("foos", "baz") 1196 | assert_equal 1, r.pfadd("foos", "foobar", "foobaz") 1197 | assert_equal 0, r.pfadd("foos", "foobar", "foobaz") 1198 | assert_raise(ArgumentError) {r.pfadd } 1199 | 1200 | r.close 1201 | assert_raise(Redis::ClosedError) {r.pfadd("foos")} 1202 | end 1203 | 1204 | assert("Non-authrozied Redis#pfadd") do 1205 | r = Redis.new HOST, SECURED_PORT 1206 | assert_raise(Redis::ReplyError) {r.pfadd("foos")} 1207 | r.close 1208 | end 1209 | 1210 | assert("Redis#pfcount") do 1211 | r = Redis.new HOST, PORT 1212 | r.flushall 1213 | r.pfadd("foos", "bar") 1214 | r.pfadd("foos", "baz") 1215 | r.pfadd("bars", "foobar") 1216 | 1217 | assert_equal 2, r.pfcount("foos") 1218 | assert_equal 3, r.pfcount("foos", "bars") 1219 | assert_equal 3, r.pfcount("foos", "bars", "barz") 1220 | assert_raise(ArgumentError) {r.pfcount } 1221 | r.close 1222 | assert_raise(Redis::ClosedError) {r.pfcount("foos")} 1223 | end 1224 | 1225 | assert("Non-authrozied Redis#pfcount") do 1226 | r = Redis.new HOST, SECURED_PORT 1227 | assert_raise(Redis::ReplyError) {r.pfcount("foos")} 1228 | r.close 1229 | end 1230 | 1231 | assert("Redis#pfmerge") do 1232 | r = Redis.new HOST, PORT 1233 | r.flushall 1234 | r.pfadd("foo", "a", "b", "c") 1235 | r.pfadd("bar", "c", "d", "e") 1236 | r.pfadd("baz", "e", "f", "g") 1237 | 1238 | assert_raise(ArgumentError) {r.pfmerge } 1239 | assert_raise(ArgumentError) {r.pfmerge "foobar" } 1240 | 1241 | r.pfmerge "foos", "foo" 1242 | r.pfmerge "foobar", "foo", "bar" 1243 | r.pfmerge "foobarbaz", "foo", "bar", "baz", "foobar" 1244 | 1245 | assert_equal 3, r.pfcount("foos") 1246 | assert_equal 5, r.pfcount("foobar") 1247 | assert_equal 7, r.pfcount("foobarbaz") 1248 | r.close 1249 | assert_raise(Redis::ClosedError) {r.pfmerge "foos", "foo"} 1250 | end 1251 | 1252 | assert("Non-authrozied Redis#pfmerge") do 1253 | r = Redis.new HOST, SECURED_PORT 1254 | assert_raise(Redis::ReplyError) {r.pfmerge "foos", "foo"} 1255 | r.close 1256 | end 1257 | 1258 | assert("Redis#multi") do 1259 | client1 = Redis.new HOST, PORT 1260 | client2 = Redis.new HOST, PORT 1261 | client1.del "hoge" 1262 | 1263 | ret1 = client1.multi 1264 | client1.set "hoge", "fuga" 1265 | ret2 = client2.get "hoge" 1266 | 1267 | assert_equal "OK", ret1 1268 | assert_equal nil, ret2 1269 | 1270 | client1.discard 1271 | 1272 | client1.close 1273 | client2.close 1274 | assert_raise(Redis::ClosedError) {client1.multi} 1275 | end 1276 | 1277 | assert("Non-authrozied Redis#multi") do 1278 | r = Redis.new HOST, SECURED_PORT 1279 | assert_raise(Redis::ReplyError) {r.multi} 1280 | r.close 1281 | end 1282 | 1283 | assert("Redis#exec") do 1284 | client1 = Redis.new HOST, PORT 1285 | client2 = Redis.new HOST, PORT 1286 | client1.del "hoge" 1287 | 1288 | client1.multi 1289 | client1.set "hoge", "fuga" 1290 | ret1 = client1.exec 1291 | ret2 = client2.get "hoge" 1292 | 1293 | assert_equal ["OK"], ret1 1294 | assert_equal "fuga", ret2 1295 | 1296 | client1.close 1297 | client2.close 1298 | assert_raise(Redis::ClosedError) {client1.exec} 1299 | end 1300 | 1301 | assert("Non-authrozied Redis#exec") do 1302 | r = Redis.new HOST, SECURED_PORT 1303 | assert_raise(Redis::ReplyError) {r.exec} 1304 | r.close 1305 | end 1306 | 1307 | assert("Redis#discard") do 1308 | client1 = Redis.new HOST, PORT 1309 | client2 = Redis.new HOST, PORT 1310 | client1.del "hoge" 1311 | 1312 | client1.multi 1313 | client1.set "hoge", "fuga" 1314 | ret1 = client1.discard 1315 | ret2 = client2.get "hoge" 1316 | assert_raise(Redis::ReplyError) {client1.discard} 1317 | 1318 | assert_equal "OK", ret1 1319 | assert_equal nil, ret2 1320 | 1321 | client1.close 1322 | client2.close 1323 | assert_raise(Redis::ClosedError) {client1.discard} 1324 | end 1325 | 1326 | assert("Non-authrozied Redis#discard") do 1327 | r = Redis.new HOST, SECURED_PORT 1328 | assert_raise(Redis::ReplyError) {r.discard} 1329 | r.close 1330 | end 1331 | 1332 | assert("Redis#watch") do 1333 | client1 = Redis.new HOST, PORT 1334 | client2 = Redis.new HOST, PORT 1335 | client1.set "hoge", "1" 1336 | client1.set "fuga", "2" 1337 | 1338 | ret1 = client1.watch "hoge", "fuga" 1339 | client1.multi 1340 | client1.set "hoge", "10" 1341 | client1.set "fuga", "20" 1342 | ret2 = client1.exec 1343 | 1344 | client1.watch "hoge", "fuga" 1345 | client1.multi 1346 | client1.set "hoge", "100" 1347 | client1.set "fuga", "200" 1348 | client2.set "hoge", "-100" 1349 | ret3 = client1.exec 1350 | ret4 = client1.get "hoge" 1351 | 1352 | assert_equal "OK", ret1 1353 | assert_equal ["OK", "OK"], ret2 1354 | assert_equal nil, ret3 1355 | assert_equal "-100", ret4 1356 | 1357 | client1.close 1358 | client2.close 1359 | assert_raise(Redis::ClosedError) {client1.watch "hoge", "fuga"} 1360 | end 1361 | 1362 | assert("Non-authrozied Redis#watch") do 1363 | r = Redis.new HOST, SECURED_PORT 1364 | assert_raise(Redis::ReplyError) {r.watch "hoge", "fuga"} 1365 | r.close 1366 | end 1367 | 1368 | assert("Redis#unwatch") do 1369 | client1 = Redis.new HOST, PORT 1370 | client2 = Redis.new HOST, PORT 1371 | client1.set "hoge", "1" 1372 | client1.set "fuga", "2" 1373 | 1374 | client1.watch "hoge", "fuga" 1375 | ret1 = client1.unwatch 1376 | client1.multi 1377 | client1.set "hoge", "100" 1378 | client1.set "fuga", "200" 1379 | client2.set "hoge", "-100" 1380 | ret2 = client1.exec 1381 | 1382 | assert_equal "OK", ret1 1383 | assert_not_equal nil, ret2 1384 | 1385 | client1.close 1386 | client2.close 1387 | assert_raise(Redis::ClosedError) {client1.unwatch} 1388 | end 1389 | 1390 | assert("Non-authrozied Redis#unwatch") do 1391 | r = Redis.new HOST, SECURED_PORT 1392 | assert_raise(Redis::ReplyError) {r.unwatch} 1393 | r.close 1394 | end 1395 | 1396 | assert("Redis#auth") do 1397 | r = Redis.new HOST, SECURED_PORT 1398 | res = begin 1399 | r.ping 1400 | rescue => e 1401 | e 1402 | end 1403 | assert_kind_of Redis::ReplyError, res 1404 | assert_equal "NOAUTH Authentication required.", res.message 1405 | 1406 | assert_raise(TypeError) {r.auth nil} 1407 | 1408 | res = begin 1409 | r.auth("wrong secret") 1410 | rescue => e 1411 | e 1412 | end 1413 | assert_kind_of(Redis::AuthError, res) 1414 | assert_equal "incorrect password", res.message 1415 | 1416 | assert_equal r.auth("secret"), "OK" 1417 | assert_equal "PONG", r.ping 1418 | end 1419 | 1420 | assert("Error handling") do 1421 | res = begin 1422 | raise Redis::ProtocolError 1423 | rescue Redis::ConnectionError 1424 | false 1425 | rescue Redis::ProtocolError 1426 | true 1427 | end 1428 | assert_true res 1429 | res = begin 1430 | raise Redis::ProtocolError 1431 | rescue Redis::ProtocolError 1432 | true 1433 | rescue Redis::ConnectionError 1434 | false 1435 | end 1436 | assert_true res 1437 | end 1438 | 1439 | assert("Redis#asking") do 1440 | r = Redis.new HOST, CLUSTER_PORT 1441 | assert_equal "OK", r.asking 1442 | r.close 1443 | assert_raise(Redis::ClosedError) {r.asking} 1444 | end 1445 | 1446 | assert("Redis#cluster") do 1447 | r = Redis.new HOST, CLUSTER_PORT 1448 | nodes = r.cluster "nodes" 1449 | slots = r.cluster "slots" 1450 | 1451 | assert_kind_of(String, nodes) 1452 | assert_equal NUM_OF_CLUSTER_NODES, nodes.split("\n").length 1453 | assert_kind_of(Array, slots) 1454 | assert_equal NUM_OF_CLUSTER_NODES, slots.length 1455 | 1456 | assert_raise(Redis::ArgumentError) {r.cluster} 1457 | assert_raise(TypeError) {r.cluster nil} 1458 | 1459 | r.close 1460 | assert_raise(Redis::ClosedError) {r.cluster "info"} 1461 | end 1462 | --------------------------------------------------------------------------------