kyototycoon 0.5.6 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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