ccp 0.2.8 → 0.2.9
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/.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
|