kyototycoon 0.5.6 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changes.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## v0.6.0
2
+
3
+ * added KyotoTycoon::Cursor for cursor support
4
+ * always close socket at script exit
5
+
1
6
  ## v0.5.6
2
7
 
3
8
  * fixed for ruby 1.8.7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kyototycoon (0.5.6)
4
+ kyototycoon (0.6.0)
5
5
  msgpack
6
6
 
7
7
  GEM
data/README.markdown CHANGED
@@ -2,11 +2,13 @@ KyotoTycoon client for Ruby.
2
2
 
3
3
  # Feature / Fixture
4
4
 
5
+ * cursor object supported(v0.6.0+)
5
6
  * Usable console as `$ bin/kyototycoon-console -h localhost -p 1991` like a Sequel(v0.5.4+)
6
7
  * Always Keep-Alive connect (v0.5.0+)
7
8
  * You can choise key/value encoding from URI or Base64
8
9
  * You can use MessagePack tranparency
9
10
  * Benchmark scripts appended(they are connect to localhost:19999)
11
+ * Both Ruby versions supported 1.8.7 / 1.9.2
10
12
 
11
13
  # Install
12
14
 
@@ -106,6 +108,22 @@ KyotoTycoon client for Ruby.
106
108
  p @kt.status
107
109
  all_record_count = @kt.status['count']
108
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
+
109
127
  # Requirements
110
128
 
111
129
  - msgpack(optional)
data/Rakefile CHANGED
@@ -9,42 +9,53 @@ rescue Bundler::BundlerError => e
9
9
  end
10
10
  require 'rake'
11
11
 
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
- gem.name = "kyototycoon"
16
- gem.homepage = "http://github.com/uu59/kyototycoon-ruby"
17
- gem.license = "MIT"
18
- gem.summary = %Q{KyotoTycoon client for Ruby}
19
- gem.description = %Q{KyotoTycoon client for Ruby}
20
- gem.email = "a@tt25.org"
21
- gem.authors = ["uu59"]
22
- # Include your dependencies below. Runtime dependencies are required when using your gem,
23
- # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
- # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
- # gem.add_development_dependency 'rspec', '> 1.2.3'
12
+ def do_rspec(opts=["-c"])
13
+ system(*['rspec', opts, 'spec/'].flatten)
26
14
  end
27
- Jeweler::RubygemsDotOrgTasks.new
28
15
 
29
- require 'rspec/core'
30
- require 'rspec/core/rake_task'
31
- RSpec::Core::RakeTask.new(:spec) do |spec|
32
- spec.pattern = FileList['spec/*.rb']
16
+ desc "run rspec"
17
+ task :rspec do
18
+ do_rspec
33
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
34
26
 
35
- RSpec::Core::RakeTask.new(:rcov) do |spec|
36
- spec.pattern = 'spec/**/*_spec.rb'
37
- spec.rcov = true
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
38
31
  end
39
32
 
40
- task :default => :spec
33
+ namespace :gem do
34
+ desc "build gem"
35
+ task :build do
36
+ system("gem build kyototycoon.gemspec")
37
+ end
41
38
 
42
- require 'rake/rdoctask'
43
- Rake::RDocTask.new do |rdoc|
44
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
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")
45
48
 
46
- rdoc.rdoc_dir = 'rdoc'
47
- rdoc.title = "test #{version}"
48
- rdoc.rdoc_files.include('README*')
49
- rdoc.rdoc_files.include('lib/**/*.rb')
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
50
59
  end
60
+
61
+ task :default => ["rspec"]
data/kyototycoon.gemspec CHANGED
@@ -3,11 +3,11 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = %q{kyototycoon}
6
- s.version = "0.5.6"
6
+ s.version = "0.6.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
9
  s.authors = ["uu59"]
10
- s.date = %q{2011-07-30}
10
+ s.date = %q{2011-08-10}
11
11
  s.default_executable = %q{kyototycoon-console}
12
12
  s.description = %q{KyotoTycoon client for Ruby}
13
13
  s.email = %q{k@uu59.org}
@@ -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
+ at_exit { 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(xt=nil)
75
+ res = request('/rpc/cur_seize', {"xt" => xt})
76
+ [res["key"], res["value"]]
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
@@ -11,6 +11,7 @@ class KyotoTycoon
11
11
  @tpl << "Content-Length: %d\r\n"
12
12
  @tpl << "Content-Type: text/tab-separated-values; colenc=%s\r\n"
13
13
  @tpl << "\r\n%s"
14
+ at_exit { finish }
14
15
  end
15
16
 
16
17
  def request(path, params, colenc)
@@ -49,7 +50,7 @@ class KyotoTycoon
49
50
  end
50
51
 
51
52
  def finish
52
- @sock.close if @sock
53
+ @sock.close if @sock && !@sock.closed?
53
54
  end
54
55
  end
55
56
  end
data/lib/kyototycoon.rb CHANGED
@@ -5,6 +5,7 @@ require "cgi"
5
5
  require "socket"
6
6
  require "base64"
7
7
  require "timeout"
8
+ require "kyototycoon/cursor.rb"
8
9
  require "kyototycoon/serializer.rb"
9
10
  require "kyototycoon/serializer/default.rb"
10
11
  require "kyototycoon/serializer/msgpack.rb"
@@ -13,7 +14,7 @@ require "kyototycoon/tsvrpc/skinny.rb"
13
14
  require "kyototycoon/stream.rb"
14
15
 
15
16
  class KyotoTycoon
16
- VERSION = '0.5.6'
17
+ VERSION = '0.6.0'
17
18
 
18
19
  attr_accessor :colenc, :connect_timeout, :servers
19
20
  attr_reader :serializer, :logger, :db
@@ -55,6 +56,7 @@ class KyotoTycoon
55
56
  @logger = Logger.new(nil)
56
57
  @colenc = :B
57
58
  @connect_timeout = 0.5
59
+ @cursor = 1
58
60
  end
59
61
 
60
62
  def serializer= (adaptor=:default)
@@ -146,15 +148,11 @@ class KyotoTycoon
146
148
  params
147
149
  }
148
150
  res = request('/rpc/get_bulk', params)
149
- ret = {}
150
151
  bulk = Tsvrpc.parse(res[:body], res[:colenc])
151
- bulk = bulk.find_all{|k,v| k.match(/^_/)}.inject({}){|r,kv| r[kv.first] = kv.last; r}
152
- ret = bulk.reduce({}) do |hash, (k,v)|
153
- key = k.match(/^_/) ? k[1..-1] : k
154
- hash[key] = @serializer.decode(v)
155
- hash
156
- end
157
- ret
152
+ bulk.delete_if{|k,v| k.match(/^[^_]/)}.inject({}){|r, (k,v)|
153
+ r[k[1..-1]] = @serializer.decode(v)
154
+ r
155
+ }
158
156
  end
159
157
 
160
158
  def remove_bulk(keys)
@@ -166,6 +164,10 @@ class KyotoTycoon
166
164
  Tsvrpc.parse(res[:body], res[:colenc])
167
165
  end
168
166
 
167
+ def cursor(cur_id=nil)
168
+ Cursor.new(self, cur_id || @cursor += 1)
169
+ end
170
+
169
171
  def clear
170
172
  request('/rpc/clear')
171
173
  end
@@ -0,0 +1,89 @@
1
+ # -- coding: utf-8
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper.rb')
4
+
5
+ describe KyotoTycoon do
6
+ before(:each) do
7
+ @kt = KyotoTycoon.new('localhost', 19999)
8
+ 100.times{|n|
9
+ @kt["#{"%02d" % n}foo"] = "foo#{n}"
10
+ }
11
+ end
12
+
13
+ it 'should handle cursor object' do
14
+ cur = @kt.cursor
15
+ cur.jump("33")
16
+ cur.key.should == "33foo"
17
+ cur.value.should == "foo33"
18
+ cur.current.should == [ "33foo","foo33" ]
19
+ cur.value = "new"
20
+ @kt["33foo"].should == "new"
21
+
22
+ cur.step
23
+ cur.key.should == "34foo"
24
+ cur.value.should == "foo34"
25
+ cur.remove
26
+ @kt["34foo"].should be_nil
27
+
28
+ cur.jump('55')
29
+ cur.seize.should == ["55foo","foo55"]
30
+ @kt["55foo"].should be_nil
31
+ end
32
+
33
+ it 'should handle cursor steps' do
34
+ # If you got failed in this section, it's a KyotoCabinet's bug
35
+ # It has been fixed at KyotoCabinet 1.2.70
36
+ # c.f.
37
+ # https://gist.github.com/1117611
38
+ # https://twitter.com/#!/fallabs/status/98079688550916097
39
+ cur = @kt.cursor
40
+ cur.jump
41
+ cur.key.should == "00foo"
42
+
43
+ cur.jump_back
44
+ cur.key.should == "99foo"
45
+ cur.jump
46
+ cur.key.should == "00foo"
47
+
48
+ cur.jump("50")
49
+ cur.step
50
+ cur.key.should == "51foo"
51
+ cur.step_back
52
+ cur.key.should == "50foo"
53
+ cur.step
54
+ cur.key.should == "51foo"
55
+ end
56
+
57
+ it "should handle multiple cursors" do
58
+ cur = @kt.cursor
59
+ cur2 = @kt.cursor
60
+ cur.jump("33")
61
+ cur.cur.should_not == cur2.cur
62
+ cur.key.should_not == cur2.key
63
+ end
64
+
65
+ it "should keep current position after called #each" do
66
+ cur = @kt.cursor
67
+ cur.jump("49")
68
+ cur.each{|k,v| } # no-op
69
+ cur.key.should == "49foo"
70
+ end
71
+
72
+ it "should handle #each" do
73
+ cur = @kt.cursor
74
+ cur.find{|k,v| k == "non-exists"}.should be_nil
75
+ cur.find_all{|k,v| k.match(/3[234]foo/)}.should == [
76
+ %w!32foo foo32!,
77
+ %w!33foo foo33!,
78
+ %w!34foo foo34!,
79
+ ]
80
+ cur.jump("33")
81
+ cur.find_all{|k,v| k.match(/3[234]foo/)}.should == [
82
+ %w!33foo foo33!,
83
+ %w!34foo foo34!,
84
+ ]
85
+
86
+ cur.jump
87
+ cur.find{|k,v| k == "15foo"}.should == ["15foo", "foo15"]
88
+ end
89
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,7 +7,7 @@
7
7
  !!!!!!!!!!!!!
8
8
 
9
9
  This script access http://0.0.0.0:19999/ and destroy all records.
10
- Be carefully for run, and run `ktserver -port 19999 '*'` before testing.
10
+ Be carefully for run, and run `ktserver -port 19999 '%'` before testing.
11
11
 
12
12
  =end
13
13
 
metadata CHANGED
@@ -1,108 +1,80 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: kyototycoon
3
- version: !ruby/object:Gem::Version
4
- hash: 7
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 5
9
- - 6
10
- version: 0.5.6
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - uu59
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-07-30 00:00:00 +10:00
12
+ date: 2011-08-10 00:00:00.000000000 +10:00
19
13
  default_executable: kyototycoon-console
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: msgpack
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &2152635020 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
33
23
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: rspec
37
24
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *2152635020
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &2152632340 !ruby/object:Gem::Requirement
39
29
  none: false
40
- requirements:
30
+ requirements:
41
31
  - - ~>
42
- - !ruby/object:Gem::Version
43
- hash: 11
44
- segments:
45
- - 2
46
- - 1
47
- - 0
32
+ - !ruby/object:Gem::Version
48
33
  version: 2.1.0
49
34
  type: :development
50
- version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: bundler
53
35
  prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
36
+ version_requirements: *2152632340
37
+ - !ruby/object:Gem::Dependency
38
+ name: bundler
39
+ requirement: &2152631480 !ruby/object:Gem::Requirement
55
40
  none: false
56
- requirements:
41
+ requirements:
57
42
  - - ~>
58
- - !ruby/object:Gem::Version
59
- hash: 23
60
- segments:
61
- - 1
62
- - 0
63
- - 0
43
+ - !ruby/object:Gem::Version
64
44
  version: 1.0.0
65
45
  type: :development
66
- version_requirements: *id003
67
- - !ruby/object:Gem::Dependency
68
- name: jeweler
69
46
  prerelease: false
70
- requirement: &id004 !ruby/object:Gem::Requirement
47
+ version_requirements: *2152631480
48
+ - !ruby/object:Gem::Dependency
49
+ name: jeweler
50
+ requirement: &2152627020 !ruby/object:Gem::Requirement
71
51
  none: false
72
- requirements:
52
+ requirements:
73
53
  - - ~>
74
- - !ruby/object:Gem::Version
75
- hash: 1
76
- segments:
77
- - 1
78
- - 5
79
- - 1
54
+ - !ruby/object:Gem::Version
80
55
  version: 1.5.1
81
56
  type: :development
82
- version_requirements: *id004
83
- - !ruby/object:Gem::Dependency
84
- name: simplecov
85
57
  prerelease: false
86
- requirement: &id005 !ruby/object:Gem::Requirement
58
+ version_requirements: *2152627020
59
+ - !ruby/object:Gem::Dependency
60
+ name: simplecov
61
+ requirement: &2152621760 !ruby/object:Gem::Requirement
87
62
  none: false
88
- requirements:
89
- - - ">="
90
- - !ruby/object:Gem::Version
91
- hash: 3
92
- segments:
93
- - 0
94
- version: "0"
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
95
67
  type: :development
96
- version_requirements: *id005
68
+ prerelease: false
69
+ version_requirements: *2152621760
97
70
  description: KyotoTycoon client for Ruby
98
71
  email: k@uu59.org
99
- executables:
72
+ executables:
100
73
  - kyototycoon-console
101
74
  extensions: []
102
-
103
- extra_rdoc_files:
75
+ extra_rdoc_files:
104
76
  - README.markdown
105
- files:
77
+ files:
106
78
  - .gitignore
107
79
  - Changes.md
108
80
  - Gemfile
@@ -119,6 +91,7 @@ files:
119
91
  - bin/kyototycoon-console
120
92
  - kyototycoon.gemspec
121
93
  - lib/kyototycoon.rb
94
+ - lib/kyototycoon/cursor.rb
122
95
  - lib/kyototycoon/serializer.rb
123
96
  - lib/kyototycoon/serializer/default.rb
124
97
  - lib/kyototycoon/serializer/msgpack.rb
@@ -126,45 +99,42 @@ files:
126
99
  - lib/kyototycoon/tsvrpc.rb
127
100
  - lib/kyototycoon/tsvrpc/skinny.rb
128
101
  - spec/connect_spec.rb
102
+ - spec/cursor_spec.rb
129
103
  - spec/ktslave.txt
130
104
  - spec/options_spec.rb
131
105
  - spec/spec_helper.rb
132
106
  has_rdoc: true
133
107
  homepage: http://github.com/uu59/kyototycoon-ruby
134
- licenses:
108
+ licenses:
135
109
  - MIT
136
110
  post_install_message:
137
111
  rdoc_options: []
138
-
139
- require_paths:
112
+ require_paths:
140
113
  - lib
141
- required_ruby_version: !ruby/object:Gem::Requirement
114
+ required_ruby_version: !ruby/object:Gem::Requirement
142
115
  none: false
143
- requirements:
144
- - - ">="
145
- - !ruby/object:Gem::Version
146
- hash: 3
147
- segments:
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ segments:
148
121
  - 0
149
- version: "0"
150
- required_rubygems_version: !ruby/object:Gem::Requirement
122
+ hash: -4066968270942491999
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
124
  none: false
152
- requirements:
153
- - - ">="
154
- - !ruby/object:Gem::Version
155
- hash: 3
156
- segments:
157
- - 0
158
- version: "0"
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
159
129
  requirements: []
160
-
161
130
  rubyforge_project:
162
131
  rubygems_version: 1.6.2
163
132
  signing_key:
164
133
  specification_version: 3
165
134
  summary: KyotoTycoon client for Ruby
166
- test_files:
135
+ test_files:
167
136
  - spec/connect_spec.rb
137
+ - spec/cursor_spec.rb
168
138
  - spec/ktslave.txt
169
139
  - spec/options_spec.rb
170
140
  - spec/spec_helper.rb