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 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