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 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
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
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)
@@ -0,0 +1,12 @@
1
+ # -- coding: utf-8
2
+
3
+ require File.expand_path("#{File.dirname(__FILE__)}/helper.rb")
4
+
5
+ job = lambda {|kt|
6
+ 1000.times{|n|
7
+ kt.set(n.to_s, n)
8
+ kt.get(n)
9
+ }
10
+ kt.clear
11
+ }
12
+ benchmark(job)
@@ -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)
@@ -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
@@ -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,15 @@
1
+ # -- coding: utf-8
2
+
3
+ class KyotoTycoon
4
+ module Serializer
5
+ class Default
6
+ def self.encode(obj)
7
+ obj
8
+ end
9
+
10
+ def self.decode(str)
11
+ str
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -0,0 +1,9 @@
1
+ # -- coding: utf-8
2
+
3
+ class KyotoTycoon
4
+ module Serializer
5
+ def self.get(adaptor)
6
+ const_get(adaptor.to_s.capitalize)
7
+ end
8
+ end
9
+ end