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 CHANGED
@@ -3,3 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  tmp/*
6
+ vendor
@@ -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.7"
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
@@ -14,4 +14,5 @@ module Ccp
14
14
  autoload :Receivers, 'ccp/receivers'
15
15
  autoload :Persistent, 'ccp/persistent'
16
16
  autoload :Serializers, 'ccp/serializers'
17
+ autoload :Kvs, 'ccp/kvs'
17
18
  end
@@ -2,9 +2,8 @@ module Ccp
2
2
  module Commands
3
3
  module Resolvable
4
4
  def resolve(klass)
5
- klass.must.coerced(Class, Module) {
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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,12 @@
1
+ require 'tokyocabinet'
2
+
3
+ module Ccp
4
+ module Kvs
5
+ class Tch < Tokyo::Cabinet
6
+ def get(k) ; R{ super }; end
7
+ def set(k,v) ; W{ super }; end
8
+ def del(k) ; W{ super }; end
9
+ def count ; R{ super }; end
10
+ end
11
+ end
12
+ end
@@ -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