ccp 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/ccp.gemspec +11 -1
- data/lib/ccp.rb +1 -0
- data/lib/ccp/commands/resolvable.rb +1 -2
- data/lib/ccp/kvs.rb +46 -0
- data/lib/ccp/kvs/core.rb +28 -0
- data/lib/ccp/kvs/hash.rb +16 -0
- data/lib/ccp/kvs/tch.rb +12 -0
- data/lib/ccp/kvs/tokyo.rb +15 -0
- data/lib/ccp/kvs/tokyo/base.rb +49 -0
- data/lib/ccp/kvs/tokyo/cabinet.rb +56 -0
- data/lib/ccp/kvs/tokyo/info.rb +74 -0
- data/lib/ccp/kvs/tokyo/state_machine.rb +106 -0
- data/lib/ccp/serializers.rb +35 -16
- data/lib/ccp/serializers/core.rb +13 -10
- data/lib/ccp/serializers/json.rb +15 -11
- data/lib/ccp/serializers/msgpack.rb +12 -0
- data/lib/ccp/serializers/yaml.rb +8 -11
- data/lib/ccp/version.rb +1 -1
- data/spec/kvs/core_spec.rb +12 -0
- data/spec/kvs/kvs_spec.rb +44 -0
- data/spec/kvs/lookup_spec.rb +55 -0
- data/spec/kvs/tch_spec.rb +19 -0
- data/spec/kvs/tokyo/cabinet_spec.rb +208 -0
- data/spec/kvs/tokyo/info_spec.rb +27 -0
- data/spec/serializers/core_spec.rb +0 -1
- data/spec/serializers/lookup_spec.rb +86 -0
- data/spec/serializers/serializer_spec.rb +34 -0
- data/spec/spec_helper.rb +21 -1
- metadata +108 -18
- data/spec/serializers/json_spec.rb +0 -27
- data/spec/serializers/spec.rb +0 -39
- data/spec/serializers/yaml_spec.rb +0 -27
data/.gitignore
CHANGED
data/ccp.gemspec
CHANGED
@@ -18,9 +18,19 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
+
if RUBY_VERSION >= "1.9"
|
22
|
+
s.add_dependency "activesupport"
|
23
|
+
else
|
24
|
+
s.add_dependency "activesupport", "~> 3.2.0"
|
25
|
+
end
|
26
|
+
|
21
27
|
s.add_dependency "typed", ">= 0.2.2"
|
22
|
-
s.add_dependency "must", ">= 0.2.
|
28
|
+
s.add_dependency "must", ">= 0.2.9"
|
23
29
|
s.add_dependency "dsl_accessor", ">= 0.4.1"
|
30
|
+
s.add_dependency "json"
|
31
|
+
s.add_dependency "yajl-ruby"
|
32
|
+
s.add_dependency "msgpack", "> 0.4"
|
33
|
+
s.add_dependency "tokyocabinet", "~> 1.29.1"
|
24
34
|
|
25
35
|
s.add_development_dependency "rspec"
|
26
36
|
end
|
data/lib/ccp.rb
CHANGED
@@ -2,9 +2,8 @@ module Ccp
|
|
2
2
|
module Commands
|
3
3
|
module Resolvable
|
4
4
|
def resolve(klass)
|
5
|
-
klass.
|
5
|
+
klass.is_a?(Class) or
|
6
6
|
raise CommandNotFound, "expected Class or Module, but got #{klass.class}"
|
7
|
-
}
|
8
7
|
|
9
8
|
if klass.ancestors.include?(Commands::Core)
|
10
9
|
return klass # ok
|
data/lib/ccp/kvs.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ccp/kvs/core'
|
2
|
+
|
3
|
+
module Ccp
|
4
|
+
module Kvs
|
5
|
+
Error = Class.new(RuntimeError)
|
6
|
+
NotFound = Class.new(Error)
|
7
|
+
NotConnected = Class.new(Error)
|
8
|
+
NotAllowed = Class.new(Error)
|
9
|
+
IOError = Class.new(Error)
|
10
|
+
|
11
|
+
DICTIONARY = {} # cache for (extname -> Kvs)
|
12
|
+
|
13
|
+
include Enumerable
|
14
|
+
delegate :delete, :to=>"DICTIONARY"
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
DICTIONARY.each_value(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](name)
|
21
|
+
kvs = DICTIONARY[name.to_s] and return kvs
|
22
|
+
name.must(Core) {
|
23
|
+
raise NotFound, "%s(%s) for %s" % [name, name.class, DICTIONARY.keys.inspect]
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(key, val)
|
28
|
+
DICTIONARY[key.to_s] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
def <<(kvs)
|
32
|
+
kvs.must(Core)
|
33
|
+
self[kvs.ext] = kvs
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :lookup :[]
|
37
|
+
extend self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
require 'ccp/kvs/hash'
|
42
|
+
require 'ccp/kvs/tokyo'
|
43
|
+
require 'ccp/kvs/tch'
|
44
|
+
|
45
|
+
Ccp::Kvs << Ccp::Kvs::Hash
|
46
|
+
Ccp::Kvs << Ccp::Kvs::Tch
|
data/lib/ccp/kvs/core.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
module Core
|
4
|
+
def get(k) ; raise NotImplementedError, "subclass resposibility"; end
|
5
|
+
def set(k,v) ; raise NotImplementedError, "subclass resposibility"; end
|
6
|
+
def del(k) ; raise NotImplementedError, "subclass resposibility"; end
|
7
|
+
|
8
|
+
def open(*) ; end
|
9
|
+
def close ; end
|
10
|
+
def source ; @source; end
|
11
|
+
def count ; end
|
12
|
+
def touch ; end
|
13
|
+
|
14
|
+
def [](k) ; get(k) ; end
|
15
|
+
def []=(k,v) ; set(k,v) ; end
|
16
|
+
def put(k,v) ; set(k,v) ; end
|
17
|
+
def out(k) ; del(k) ; end
|
18
|
+
|
19
|
+
def ext; self.class.name.split(/::/).last.to_s.downcase; end
|
20
|
+
def self.included(klass)
|
21
|
+
klass.class_eval do
|
22
|
+
def self.ext; name.split(/::/).last.to_s.downcase; end
|
23
|
+
def self.open(*args); new.tap{|kvs| kvs.open(*args)}; end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/ccp/kvs/hash.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
class Hash
|
4
|
+
include Core
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@db = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def get(k) ; @db[k.to_s] ; end
|
11
|
+
def set(k,v) ; @db[k.to_s] = v.to_s ; end
|
12
|
+
def del(k) ; @db.delete(k.to_s) ; end
|
13
|
+
def count ; @db.size ; end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/ccp/kvs/tch.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'tokyocabinet'
|
2
|
+
|
3
|
+
module Ccp
|
4
|
+
module Kvs
|
5
|
+
module Tokyo
|
6
|
+
Error = Class.new(Ccp::Kvs::Error)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'ccp/kvs/tokyo/info'
|
12
|
+
require 'ccp/kvs/tokyo/base'
|
13
|
+
require 'ccp/kvs/tokyo/state_machine'
|
14
|
+
require 'ccp/kvs/tokyo/cabinet'
|
15
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
module Tokyo
|
4
|
+
class Base
|
5
|
+
include Ccp::Kvs::Core
|
6
|
+
include TokyoCabinet
|
7
|
+
|
8
|
+
######################################################################
|
9
|
+
### info
|
10
|
+
|
11
|
+
def info
|
12
|
+
if path.exist?
|
13
|
+
Tokyo::Info.parse(`tcamgr inform #{path}`)
|
14
|
+
else
|
15
|
+
raise Ccp::Kvs::NotConnected, "%s(%s)" % [ self.class, @source]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
######################################################################
|
20
|
+
### kvs
|
21
|
+
|
22
|
+
def get(k) ; @db[k] ; end
|
23
|
+
def set(k,v) ; @db[k] = v ; end
|
24
|
+
def del(k) ; @db.delete(k) ; end
|
25
|
+
|
26
|
+
def path
|
27
|
+
file = @source.to_s.sub(/#.*$/, '') # parse "foo.tch#mode=r"
|
28
|
+
Pathname(file)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def tokyo_error!(label = nil)
|
33
|
+
raise Ccp::Kvs::Tokyo::Error, "%s%s" % [label, error_message]
|
34
|
+
end
|
35
|
+
|
36
|
+
def error_message
|
37
|
+
if @db
|
38
|
+
# TODO: Where is adb_errmsg?
|
39
|
+
"%s (%s)" % [@db.errmsg(@db.ecode).to_s, @db.ecode]
|
40
|
+
else
|
41
|
+
'[Not Initialized]'
|
42
|
+
end
|
43
|
+
rescue Exception => e
|
44
|
+
"[BUG] #{e}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
module Tokyo
|
4
|
+
class Cabinet < Base
|
5
|
+
include StateMachine
|
6
|
+
|
7
|
+
def initialize(source)
|
8
|
+
@source = source
|
9
|
+
@db = HDB.new
|
10
|
+
end
|
11
|
+
|
12
|
+
######################################################################
|
13
|
+
### kvs
|
14
|
+
|
15
|
+
def get(k)
|
16
|
+
tryR("get")
|
17
|
+
v = @db[k.to_s]
|
18
|
+
if v
|
19
|
+
return v
|
20
|
+
else
|
21
|
+
if @db.ecode == HDB::ENOREC
|
22
|
+
return nil
|
23
|
+
else
|
24
|
+
tokyo_error!("get(%s): " % k)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(k,v)
|
30
|
+
tryW("set")
|
31
|
+
@db[k.to_s] = v.to_s or tokyo_error!("set(%s): " % k)
|
32
|
+
end
|
33
|
+
|
34
|
+
def del(k)
|
35
|
+
tryW("del")
|
36
|
+
v = get(k)
|
37
|
+
if v
|
38
|
+
if @db.delete(k.to_s)
|
39
|
+
return v
|
40
|
+
else
|
41
|
+
tokyo_error!("del(%s): " % k)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def count
|
49
|
+
tryR("count")
|
50
|
+
return @db.rnum
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
module Tokyo
|
4
|
+
class Info
|
5
|
+
######################################################################
|
6
|
+
### API
|
7
|
+
|
8
|
+
dsl_accessor :path , "to_s"
|
9
|
+
dsl_accessor :database_type, "to_s"
|
10
|
+
dsl_accessor :record_number, "to_i"
|
11
|
+
dsl_accessor :size , "to_i"
|
12
|
+
|
13
|
+
def count; record_number; end
|
14
|
+
|
15
|
+
######################################################################
|
16
|
+
### example
|
17
|
+
|
18
|
+
def self.example
|
19
|
+
parse(<<-EOF)
|
20
|
+
path: /tmp/tc/foo.tch
|
21
|
+
database type: hash database
|
22
|
+
record number: 0
|
23
|
+
size: 528704
|
24
|
+
EOF
|
25
|
+
end
|
26
|
+
|
27
|
+
######################################################################
|
28
|
+
### parse
|
29
|
+
|
30
|
+
def self.parse(buf)
|
31
|
+
# % tcamgr inform /tmp/tc/foo.tch
|
32
|
+
#
|
33
|
+
# path: /tmp/tc/foo.tch
|
34
|
+
# database type: hash database
|
35
|
+
# record number: 0
|
36
|
+
# size: 528704
|
37
|
+
#
|
38
|
+
|
39
|
+
info = new
|
40
|
+
buf.scan(/^([a-z ]+): (.*?)($|\Z)/mo).each do |key, val|
|
41
|
+
key = key.strip.tr(' ', '_')
|
42
|
+
val = val.strip
|
43
|
+
info[key] = val
|
44
|
+
end
|
45
|
+
return info
|
46
|
+
end
|
47
|
+
|
48
|
+
######################################################################
|
49
|
+
### instance methods
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@hash = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def [](key)
|
56
|
+
@hash[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
def []=(key, val)
|
60
|
+
@hash[key] = val
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def method_missing(key, *args, &block)
|
65
|
+
raise unless args.empty?
|
66
|
+
raise unless self.class.respond_to?(key)
|
67
|
+
|
68
|
+
cast = self.class.__send__(key)
|
69
|
+
return self[key.to_s].__send__(cast)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Kvs
|
3
|
+
module Tokyo
|
4
|
+
module StateMachine
|
5
|
+
include TokyoCabinet
|
6
|
+
|
7
|
+
######################################################################
|
8
|
+
### state machine
|
9
|
+
|
10
|
+
CLOSED = 1
|
11
|
+
READABLE = 2
|
12
|
+
WRITABLE = 3
|
13
|
+
|
14
|
+
def state
|
15
|
+
@state || CLOSED
|
16
|
+
end
|
17
|
+
|
18
|
+
def open(mode)
|
19
|
+
Pathname(@source.to_s).parent.mkpath
|
20
|
+
@db.open(@source.to_s, mode) or tokyo_error!("%s#open(%s,%s): " % [self.class, @source, mode])
|
21
|
+
end
|
22
|
+
|
23
|
+
def close
|
24
|
+
C!
|
25
|
+
end
|
26
|
+
|
27
|
+
def C!
|
28
|
+
case state
|
29
|
+
when CLOSED ; # NOP
|
30
|
+
when READABLE,
|
31
|
+
WRITABLE ; @db.close; @state = CLOSED
|
32
|
+
else ; raise "unknown state: #{state}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def R!
|
37
|
+
case state
|
38
|
+
when CLOSED ; open(HDB::OREADER); @state = READABLE
|
39
|
+
when READABLE ; # NOP
|
40
|
+
when WRITABLE ; # NOP
|
41
|
+
else ; raise "unknown state: #{state}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def W!
|
46
|
+
case state
|
47
|
+
when CLOSED ; open(HDB::OCREAT | HDB::OWRITER); @state = WRITABLE
|
48
|
+
when READABLE ; C!; W!()
|
49
|
+
when WRITABLE ; # NOP
|
50
|
+
else ; raise "unknown state: #{state}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def R(&block)
|
55
|
+
case state
|
56
|
+
when CLOSED ; begin; R!(); yield; ensure; close; end
|
57
|
+
when READABLE ; yield
|
58
|
+
when WRITABLE ; yield
|
59
|
+
else ; raise "unknown state: #{state}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def W(&block)
|
64
|
+
case state
|
65
|
+
when CLOSED ; begin; W!(); yield; ensure; close; end
|
66
|
+
when READABLE ; raise "reopen from read to write is not permitted"
|
67
|
+
when WRITABLE ; yield
|
68
|
+
else ; raise "unknown state: #{state}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def touch
|
73
|
+
W() {}
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def isReadable
|
78
|
+
case state
|
79
|
+
when CLOSED ; false
|
80
|
+
when READABLE ; true
|
81
|
+
when WRITABLE ; true
|
82
|
+
else ; raise "unknown state: #{state}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def isWritable
|
87
|
+
case state
|
88
|
+
when CLOSED ; false
|
89
|
+
when READABLE ; false
|
90
|
+
when WRITABLE ; true
|
91
|
+
else ; raise "unknown state: #{state}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def tryR(op)
|
96
|
+
isReadable or raise NotAllowed, "use R! or R{tch.%s} (%s)" % [op, source]
|
97
|
+
end
|
98
|
+
|
99
|
+
def tryW(op)
|
100
|
+
isWritable or raise NotAllowed, "use W! or W{tch.%s} (%s)" % [op, source]
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|