genki-kyototycoon 0.6.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Changes.md +52 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +38 -0
- data/MIT-LICENSE +22 -0
- data/README.markdown +155 -0
- data/Rakefile +61 -0
- data/VERSION +1 -0
- data/benchmark/bulk.rb +15 -0
- data/benchmark/bulk_bigdata.rb +15 -0
- data/benchmark/getset.rb +12 -0
- data/benchmark/getset_while_1sec.rb +21 -0
- data/benchmark/helper.rb +22 -0
- data/bin/kyototycoon-console +26 -0
- data/kyototycoon.gemspec +49 -0
- data/lib/kyototycoon/cursor.rb +96 -0
- data/lib/kyototycoon/serializer/default.rb +15 -0
- data/lib/kyototycoon/serializer/msgpack.rb +21 -0
- data/lib/kyototycoon/serializer.rb +9 -0
- data/lib/kyototycoon/stream.rb +74 -0
- data/lib/kyototycoon/tsvrpc/skinny.rb +57 -0
- data/lib/kyototycoon/tsvrpc.rb +45 -0
- data/lib/kyototycoon.rb +305 -0
- data/spec/connect_spec.rb +72 -0
- data/spec/cursor_spec.rb +89 -0
- data/spec/ktslave.txt +4 -0
- data/spec/options_spec.rb +132 -0
- data/spec/spec_helper.rb +37 -0
- metadata +174 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
coverage/
|
data/Changes.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
## v0.6.0
|
2
|
+
|
3
|
+
* added KyotoTycoon::Cursor for cursor support
|
4
|
+
* always close socket at script exit
|
5
|
+
|
6
|
+
## v0.5.6
|
7
|
+
|
8
|
+
* fixed for ruby 1.8.7
|
9
|
+
|
10
|
+
## v0.5.5
|
11
|
+
|
12
|
+
* fixed bulk methods. [Thanks rickyrobinson!](https://github.com/uu59/kyototycoon-ruby/pull/1/files)
|
13
|
+
|
14
|
+
## v0.5.4
|
15
|
+
|
16
|
+
* Added bin/kyototycoon-console. it's like as Sequel's `sequel` script
|
17
|
+
|
18
|
+
## v0.5.3
|
19
|
+
|
20
|
+
* fixed bug using base64 encoding
|
21
|
+
* regenerate socket when that closed
|
22
|
+
|
23
|
+
## v0.5.2
|
24
|
+
|
25
|
+
* fixed encoded response handling
|
26
|
+
* fixed miss named method
|
27
|
+
|
28
|
+
## v0.5.1
|
29
|
+
|
30
|
+
* changing server dicision logic
|
31
|
+
* added KT::Stream.run
|
32
|
+
|
33
|
+
## v0.5.0
|
34
|
+
|
35
|
+
* Always Keep-Alive connection
|
36
|
+
* remove Tsvrpc::Nethttp, and KT#agent=
|
37
|
+
* default KT#colenc = :B (Base64)
|
38
|
+
* modified benchmark/*.rb
|
39
|
+
|
40
|
+
## v0.1.2
|
41
|
+
|
42
|
+
* added KT#configure, KT#create
|
43
|
+
* added KT#incr, KT#decr, KT#decrement
|
44
|
+
* rspec connect to localhost:19999 for safety
|
45
|
+
|
46
|
+
## v0.1.1
|
47
|
+
|
48
|
+
* fixed always xt=0
|
49
|
+
|
50
|
+
## v0.1.0
|
51
|
+
|
52
|
+
* first release
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
genki-kyototycoon (0.6.0.1)
|
5
|
+
msgpack
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
git (1.2.5)
|
12
|
+
jeweler (1.5.2)
|
13
|
+
bundler (~> 1.0.0)
|
14
|
+
git (>= 1.2.5)
|
15
|
+
rake
|
16
|
+
msgpack (0.4.6)
|
17
|
+
rake (0.9.2)
|
18
|
+
rspec (2.1.0)
|
19
|
+
rspec-core (~> 2.1.0)
|
20
|
+
rspec-expectations (~> 2.1.0)
|
21
|
+
rspec-mocks (~> 2.1.0)
|
22
|
+
rspec-core (2.1.0)
|
23
|
+
rspec-expectations (2.1.0)
|
24
|
+
diff-lcs (~> 1.1.2)
|
25
|
+
rspec-mocks (2.1.0)
|
26
|
+
simplecov (0.4.2)
|
27
|
+
simplecov-html (~> 0.4.4)
|
28
|
+
simplecov-html (0.4.5)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
bundler (~> 1.0.0)
|
35
|
+
genki-kyototycoon!
|
36
|
+
jeweler (~> 1.5.1)
|
37
|
+
rspec (~> 2.1.0)
|
38
|
+
simplecov
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011 uu59<a@tt25.org>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
KyotoTycoon client for Ruby.
|
2
|
+
|
3
|
+
# Feature / Fixture
|
4
|
+
|
5
|
+
* cursor object supported(v0.6.0+)
|
6
|
+
* Usable console as `$ bin/kyototycoon-console -h localhost -p 1991` like a Sequel(v0.5.4+)
|
7
|
+
* Always Keep-Alive connect (v0.5.0+)
|
8
|
+
* You can choise key/value encoding from URI or Base64
|
9
|
+
* You can use MessagePack tranparency
|
10
|
+
* Benchmark scripts appended(they are connect to localhost:19999)
|
11
|
+
* Both Ruby versions supported 1.8.7 / 1.9.2
|
12
|
+
|
13
|
+
# Install
|
14
|
+
|
15
|
+
$ gem install kyototycoon
|
16
|
+
|
17
|
+
# Example
|
18
|
+
|
19
|
+
## Simple case
|
20
|
+
|
21
|
+
@kt = KyotoTycoon.new('localhost', 1978)
|
22
|
+
|
23
|
+
# traditional style
|
24
|
+
@kt.set('foo', 123)
|
25
|
+
p @kt.get('foo') # => "123". carefully, it is String, not Integer, by default
|
26
|
+
|
27
|
+
# Ruby's hash style
|
28
|
+
@kt['bar'] = 42
|
29
|
+
p @kt['bar'] # => "42".
|
30
|
+
@kt[:baz] = :aaa # => key/value are automatically #to_s
|
31
|
+
|
32
|
+
@kt.delete(:foo)
|
33
|
+
|
34
|
+
|
35
|
+
## Complex case
|
36
|
+
# KT#configure for instance setting store.
|
37
|
+
|
38
|
+
KyotoTycoon.configure(:generic) do |kt|
|
39
|
+
kt.db = '*' # on memory
|
40
|
+
end
|
41
|
+
|
42
|
+
# connect any host, any port
|
43
|
+
KyotoTycoon.configure(:favicon, 'remotehost', 12345) do |kt|
|
44
|
+
kt.db = 'favicons.kch' # DB file as KT known
|
45
|
+
end
|
46
|
+
|
47
|
+
@kt = KyotoTycoon.create(:generic) # got KT instance by KT#configure(:generic) rules
|
48
|
+
|
49
|
+
# set/bulk_set/get/bulk_get uses msgpack. default as :default
|
50
|
+
@kt.serializer = :msgpack
|
51
|
+
|
52
|
+
# KT library logging
|
53
|
+
logger = Logger.new(STDERR)
|
54
|
+
logger.level = Logger::WARN
|
55
|
+
@kt.logger = logger
|
56
|
+
# or you can use these:
|
57
|
+
# @kt.logger = 'ktlib.log'
|
58
|
+
# @kt.logger = STDOUT
|
59
|
+
# @kt.logger = Logger.new(STDOUT)
|
60
|
+
|
61
|
+
# standby server
|
62
|
+
@kt.connect_timeout = 0.5 # => wait 0.5 sec for connection open
|
63
|
+
@kt.servers << ['server2', 1978] # standby server that will use when primary server (a.k.a. KT#new(host, port)) is dead.
|
64
|
+
@kt.servers << ['server3', 1978] # same as above
|
65
|
+
|
66
|
+
# key/value encoding from :U or :B(default). default as base64 because it seems better than URL encode for me.
|
67
|
+
@kt.colenc = :U
|
68
|
+
|
69
|
+
# get/set
|
70
|
+
@kt.set('foo', 42, 100) # => expire at 100 seconds after
|
71
|
+
@kt['foo'] # => 42. it is interger by msgpack serializer works
|
72
|
+
|
73
|
+
# delete all record
|
74
|
+
@kt.clear
|
75
|
+
|
76
|
+
# bulk set/get
|
77
|
+
@kt.set_bulk({
|
78
|
+
'foo' => 'foo',
|
79
|
+
'bar' => 'bar',
|
80
|
+
})
|
81
|
+
@kt.get_bulk([:foo, 'bar']) # => {'_foo' => 'foo', '_bar' => 'bar', 'num' => '2'}
|
82
|
+
@kt.remove_bulk([:foo, :bar])
|
83
|
+
|
84
|
+
# it can store when msgpack using.
|
85
|
+
@kt['baz'] = {'a' => 'a', 'b' => 'b'}
|
86
|
+
@kt['baz'] # => {'a' => 'a', 'b' => 'b}
|
87
|
+
|
88
|
+
# increment
|
89
|
+
@kt.increment('bar') # => 1
|
90
|
+
@kt.increment('bar') # => 2
|
91
|
+
@kt.increment('bar', 10) # => 12
|
92
|
+
@kt.increment('bar', -5) # => 7
|
93
|
+
|
94
|
+
# shorthand
|
95
|
+
@kt.incr('foo') # => 1
|
96
|
+
@kt.decr('foo') # => 0
|
97
|
+
|
98
|
+
# delete keys
|
99
|
+
@kt.delete(:foo, :bar, :baz)
|
100
|
+
|
101
|
+
# prefix keys
|
102
|
+
@kt.match_prefix('fo') # => all start with 'fo' keys
|
103
|
+
@kt.match_regex('^fo') # => save as above
|
104
|
+
@kt.match_regex(/^fo/) # => save as above
|
105
|
+
|
106
|
+
# reporting / statistics
|
107
|
+
p @kt.report
|
108
|
+
p @kt.status
|
109
|
+
all_record_count = @kt.status['count']
|
110
|
+
|
111
|
+
# Cursor samples
|
112
|
+
|
113
|
+
For B+Tree type database only.
|
114
|
+
http://fallabs.com/kyotocabinet/spex.html#tutorial_dbchart
|
115
|
+
|
116
|
+
@kt.clear
|
117
|
+
100.times{|n|
|
118
|
+
@kt.set("%02d" % n, n) # 00, 01, 02 .. 99
|
119
|
+
}
|
120
|
+
cur = @kt.cursor
|
121
|
+
cur.jump("90")
|
122
|
+
cur.each{|k,v| puts v} # => 90, 91, 92 .. 99
|
123
|
+
|
124
|
+
cur.jump("05")
|
125
|
+
cur.find_all{|k,v| k.to_i < 10} # => 05, 06, 07, 08, 09. Because it started with 05
|
126
|
+
|
127
|
+
# Requirements
|
128
|
+
|
129
|
+
- msgpack(optional)
|
130
|
+
|
131
|
+
# Other case for `ktremotemgr slave | ...`
|
132
|
+
|
133
|
+
$ cat foo.rb
|
134
|
+
require "rubygems"
|
135
|
+
require "kyototycoon"
|
136
|
+
|
137
|
+
KyotoTycoon::Stream.run($stdin) do |line|
|
138
|
+
case line.cmd
|
139
|
+
when 'clear'
|
140
|
+
puts "all record cleared!"
|
141
|
+
when 'set'
|
142
|
+
puts "#{line.key} get #{line.value} value"
|
143
|
+
when 'remove'
|
144
|
+
puts "#{line.key} is removed at #{line.time.strftime('%Y-%m-%d %H:%M:%S')}"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
$ ktremotemgr slave -uw | ruby foo.rb
|
149
|
+
|
150
|
+
# Trap
|
151
|
+
|
152
|
+
KyotoTycoon is based on HTTP so all variable types are become String.
|
153
|
+
It means `(@kt["counter"] ||= 1) < 10` does not work by default.
|
154
|
+
|
155
|
+
Using :msgpack serializer for avoid this trap.
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'rake'
|
11
|
+
|
12
|
+
def do_rspec(opts=["-c"])
|
13
|
+
system(*['rspec', opts, 'spec/'].flatten)
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "run rspec"
|
17
|
+
task :rspec do
|
18
|
+
do_rspec
|
19
|
+
end
|
20
|
+
namespace :rspec do
|
21
|
+
desc "run rspec with coverage"
|
22
|
+
task :cov do
|
23
|
+
ENV["COV"]="1"
|
24
|
+
do_rspec
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "run rspec with all of installed versions of ruby"
|
28
|
+
task :rvm do
|
29
|
+
system("rvm exec 'ruby -e \"puts %Q!=!*48\";ruby -v;rspec -c spec/'")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
namespace :gem do
|
34
|
+
desc "build gem"
|
35
|
+
task :build do
|
36
|
+
system("gem build kyototycoon.gemspec")
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "versioning"
|
40
|
+
task :version do
|
41
|
+
ver = ENV["VER"]
|
42
|
+
if ver.nil?
|
43
|
+
puts "version is not specified."
|
44
|
+
puts "Usage: VER=x.x.x rake ..."
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
date = Time.now.strftime("%Y-%m-%d")
|
48
|
+
|
49
|
+
# Prefer GNU sed to BSD sed
|
50
|
+
sed = [`which gsed`, `which sed`].map{|s| s.strip}.join(" ").strip.split(" ").first
|
51
|
+
system("echo lib/kyototycoon.rb | xargs #{sed} -E -i \"s/VERSION = '[0-9.]+'/VERSION = '#{ver}'/g\"")
|
52
|
+
system("echo kyototycoon.gemspec | xargs #{sed} -E -i 's/s.version\s*=\s*\".*\"/s.version = \"#{ver}\"/g'")
|
53
|
+
system("echo Gemfile.lock | xargs #{sed} -E -i 's/kyototycoon \(.*?\)/kyototycoon (#{ver})/g'")
|
54
|
+
system("echo kyototycoon.gemspec | xargs #{sed} -E -i 's/s.date = .*$/s.date = %q{#{date}}/g'")
|
55
|
+
system("git add -u")
|
56
|
+
puts "= NOTICE ="
|
57
|
+
puts "ver #{ver}, edit Changes.md for what changed and commit, git tag #{ver}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
task :default => ["rspec"]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.5.4
|
data/benchmark/bulk.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
|
4
|
+
|
5
|
+
bulk={}
|
6
|
+
50000.times.map{|n|
|
7
|
+
bulk[n.to_s] = "#{n}-#{rand}"
|
8
|
+
}
|
9
|
+
job = lambda {|kt|
|
10
|
+
kt.set_bulk(bulk)
|
11
|
+
kt.get_bulk(bulk.keys)
|
12
|
+
kt.clear
|
13
|
+
}
|
14
|
+
|
15
|
+
benchmark(job)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
|
4
|
+
|
5
|
+
bulk={}
|
6
|
+
str = "string ああ" * 10000
|
7
|
+
100.times.map{|n|
|
8
|
+
bulk[n.to_s] = str
|
9
|
+
}
|
10
|
+
job = lambda {|kt|
|
11
|
+
kt.set_bulk(bulk)
|
12
|
+
kt.get_bulk(bulk.keys)
|
13
|
+
kt.clear
|
14
|
+
}
|
15
|
+
benchmark(job)
|
data/benchmark/getset.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
|
4
|
+
|
5
|
+
job = lambda {|kt|
|
6
|
+
cnt = 0
|
7
|
+
begin
|
8
|
+
timeout(1){
|
9
|
+
loop do
|
10
|
+
kt[:foo] = :bar
|
11
|
+
kt[:foo]
|
12
|
+
cnt += 1
|
13
|
+
end
|
14
|
+
}
|
15
|
+
rescue Timeout::Error
|
16
|
+
end
|
17
|
+
kt.clear
|
18
|
+
cnt
|
19
|
+
}
|
20
|
+
|
21
|
+
benchmark(job)
|
data/benchmark/helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
|
+
require 'kyototycoon.rb'
|
7
|
+
|
8
|
+
def benchmark(job)
|
9
|
+
kt = KyotoTycoon.new('0.0.0.0', 19999)
|
10
|
+
Benchmark.bm do |x|
|
11
|
+
%w!B U!.each{|colenc|
|
12
|
+
%w!default msgpack!.each{|serializer|
|
13
|
+
x.report("#{serializer}, colenc=#{colenc}") {
|
14
|
+
kt.serializer=serializer.to_sym
|
15
|
+
kt.colenc = colenc.to_sym
|
16
|
+
job.call(kt)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "irb"
|
4
|
+
require "optparse"
|
5
|
+
require "rubygems"
|
6
|
+
require File.expand_path('../lib/kyototycoon.rb', File.dirname(__FILE__))
|
7
|
+
|
8
|
+
options = {
|
9
|
+
:host => 'localhost',
|
10
|
+
:port => 1978,
|
11
|
+
}
|
12
|
+
|
13
|
+
OptionParser.new do |opts|
|
14
|
+
opts.on('-h VAL', '--host=VAL', 'hostname'){|v| options[:host] = v}
|
15
|
+
opts.on('-p VAL', '--port=VAL', 'port numver'){|v| options[:port] = v}
|
16
|
+
opts.parse! ARGV
|
17
|
+
end
|
18
|
+
|
19
|
+
KT = KyotoTycoon.new(options[:host], options[:port])
|
20
|
+
|
21
|
+
puts "="*30
|
22
|
+
puts "You can use KyotoTycoon object for KT"
|
23
|
+
puts "options are: #{options.inspect}"
|
24
|
+
puts "="*30
|
25
|
+
|
26
|
+
IRB.start
|
data/kyototycoon.gemspec
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = %q{genki-kyototycoon}
|
6
|
+
s.version = "0.6.0.1"
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
9
|
+
s.authors = ["uu59"]
|
10
|
+
s.date = %q{2011-08-10}
|
11
|
+
s.default_executable = %q{kyototycoon-console}
|
12
|
+
s.description = %q{KyotoTycoon client for Ruby}
|
13
|
+
s.email = %q{k@uu59.org}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README.markdown"
|
16
|
+
]
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.homepage = %q{http://github.com/uu59/kyototycoon-ruby}
|
21
|
+
s.licenses = ["MIT"]
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
s.rubygems_version = %q{1.6.2}
|
24
|
+
s.summary = %q{KyotoTycoon client for Ruby}
|
25
|
+
|
26
|
+
s.add_dependency(%q<msgpack>, [">= 0"])
|
27
|
+
|
28
|
+
if s.respond_to? :specification_version then
|
29
|
+
s.specification_version = 3
|
30
|
+
|
31
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
32
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.1.0"])
|
33
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
34
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
|
35
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<rspec>, ["~> 2.1.0"])
|
38
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
39
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
40
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
41
|
+
end
|
42
|
+
else
|
43
|
+
s.add_dependency(%q<rspec>, ["~> 2.1.0"])
|
44
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
45
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
|
46
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
class KyotoTycoon
|
4
|
+
class Cursor
|
5
|
+
include Enumerable
|
6
|
+
attr_reader :cur
|
7
|
+
|
8
|
+
def initialize(kt, cur)
|
9
|
+
@kt = kt
|
10
|
+
@cur = cur
|
11
|
+
ObjectSpace.define_finalizer(self){delete!}
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
return to_enum(:each) unless block_given?
|
16
|
+
jump if current == [nil,nil]
|
17
|
+
start_key = key
|
18
|
+
begin
|
19
|
+
@kt.logger.debug("cursor each start with key=#{start_key}")
|
20
|
+
loop do
|
21
|
+
tmp = current(1)
|
22
|
+
@kt.logger.debug("cursor each key=#{tmp.first}")
|
23
|
+
break if tmp == [nil,nil]
|
24
|
+
block.call(tmp)
|
25
|
+
end
|
26
|
+
ensure
|
27
|
+
jump(start_key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def jump(key=nil)
|
32
|
+
request('/rpc/cur_jump',{:key => key})
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def jump_back(key=nil)
|
37
|
+
request('/rpc/cur_jump_back',{:key => key})
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def step
|
42
|
+
request('/rpc/cur_step')
|
43
|
+
self
|
44
|
+
end
|
45
|
+
alias_method :next, :step
|
46
|
+
|
47
|
+
def step_back
|
48
|
+
request('/rpc/cur_step_back')
|
49
|
+
self
|
50
|
+
end
|
51
|
+
alias_method :prev, :step_back
|
52
|
+
|
53
|
+
def value(step=nil)
|
54
|
+
request('/rpc/cur_get_value', {"step" => step})["value"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def value=(value, xt=nil, step=nil)
|
58
|
+
request('/rpc/cur_set_value', {
|
59
|
+
"value" => value,
|
60
|
+
"xt" => xt,
|
61
|
+
"step" => step,
|
62
|
+
})
|
63
|
+
end
|
64
|
+
|
65
|
+
def key(step=nil)
|
66
|
+
request('/rpc/cur_get_key', {"step" => step})["key"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def current(step=nil)
|
70
|
+
res = request('/rpc/cur_get', {"step" => step})
|
71
|
+
[res["key"], res["value"]]
|
72
|
+
end
|
73
|
+
|
74
|
+
def seize
|
75
|
+
res = request('/rpc/cur_seize')
|
76
|
+
[res["key"], res["value"], res["xt"]]
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove
|
80
|
+
request('/rpc/cur_remove')
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete!
|
84
|
+
request('/rpc/cur_delete')
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def request(path, params={})
|
89
|
+
params.merge!({
|
90
|
+
:CUR => @cur,
|
91
|
+
})
|
92
|
+
res = @kt.request(path, params)
|
93
|
+
Tsvrpc.parse(res[:body], res[:colenc])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -- coding: utf-8
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
begin
|
5
|
+
require "msgpack"
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
class KyotoTycoon
|
10
|
+
module Serializer
|
11
|
+
class Msgpack
|
12
|
+
def self.encode(obj)
|
13
|
+
obj.to_msgpack
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.decode(str)
|
17
|
+
MessagePack.unpack(str) if str
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|