ccp 0.1.7 → 0.2.0
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/ccp.gemspec +1 -1
- data/lib/ccp.rb +4 -2
- data/lib/ccp/invokers/base.rb +2 -2
- data/lib/ccp/persistent.rb +23 -0
- data/lib/ccp/persistent/base.rb +51 -0
- data/lib/ccp/persistent/dir.rb +48 -0
- data/lib/ccp/persistent/file.rb +59 -0
- data/lib/ccp/persistent/tsv.rb +76 -0
- data/lib/ccp/persistent/versioned.rb +90 -0
- data/lib/ccp/receivers.rb +4 -1
- data/lib/ccp/receivers/base.rb +6 -7
- data/lib/ccp/receivers/commentable.rb +1 -1
- data/lib/ccp/receivers/core.rb +14 -0
- data/lib/ccp/receivers/fixtures.rb +75 -0
- data/lib/ccp/receivers/profileable.rb +1 -1
- data/lib/ccp/receivers/settings.rb +21 -0
- data/lib/ccp/receivers/variables.rb +21 -0
- data/lib/ccp/serializers.rb +19 -0
- data/lib/ccp/serializers/core.rb +13 -0
- data/lib/ccp/serializers/json.rb +15 -0
- data/lib/ccp/serializers/yaml.rb +15 -0
- data/lib/ccp/utils.rb +6 -0
- data/lib/ccp/utils/colorize.rb +15 -0
- data/lib/ccp/version.rb +1 -1
- data/spec/{commands_base_spec.rb → commands/base_spec.rb} +0 -0
- data/spec/{commands_composite_spec.rb → commands/composite_spec.rb} +0 -0
- data/spec/{commands_core_spec.rb → commands/core_spec.rb} +0 -0
- data/spec/{commands_executable_spec.rb → commands/executable_spec.rb} +0 -0
- data/spec/{invokers_spec.rb → invokers/base_spec.rb} +0 -0
- data/spec/persistent/base_spec.rb +49 -0
- data/spec/persistent/dir_spec.rb +111 -0
- data/spec/persistent/file_spec.rb +124 -0
- data/spec/persistent/tsv_spec.rb +19 -0
- data/spec/persistent/versioned_spec.rb +110 -0
- data/spec/receivers/fixture_save_spec.rb +79 -0
- data/spec/serializers/core_spec.rb +19 -0
- data/spec/serializers/json_spec.rb +27 -0
- data/spec/serializers/spec.rb +39 -0
- data/spec/serializers/yaml_spec.rb +27 -0
- metadata +39 -18
- data/lib/ccp/colorize.rb +0 -13
- data/lib/ccp/data.rb +0 -37
- data/lib/ccp/receivers/save_fixture.rb +0 -45
- data/spec/data_spec.rb +0 -18
- data/spec/save_fixture_spec.rb +0 -47
data/ccp.gemspec
CHANGED
@@ -18,7 +18,7 @@ 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
|
-
s.add_dependency "typed", ">= 0.1
|
21
|
+
s.add_dependency "typed", ">= 0.2.1"
|
22
22
|
s.add_dependency "must", ">= 0.2.7"
|
23
23
|
s.add_dependency "dsl_accessor", ">= 0.4.0"
|
24
24
|
|
data/lib/ccp.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
require "typed"
|
2
|
+
require "pathname"
|
2
3
|
require "dsl_accessor"
|
3
4
|
require "active_support/core_ext"
|
4
5
|
require "ccp/version"
|
5
|
-
require "ccp/data"
|
6
6
|
|
7
7
|
module Ccp
|
8
|
-
autoload :
|
8
|
+
autoload :Utils, 'ccp/utils'
|
9
9
|
autoload :Fixtures, 'ccp/fixtures'
|
10
10
|
autoload :Commands, 'ccp/commands'
|
11
11
|
autoload :Invokers, 'ccp/invokers'
|
12
12
|
autoload :Receivers, 'ccp/receivers'
|
13
|
+
autoload :Persistent, 'ccp/persistent'
|
14
|
+
autoload :Serializers, 'ccp/serializers'
|
13
15
|
end
|
data/lib/ccp/invokers/base.rb
CHANGED
@@ -29,8 +29,8 @@ module Ccp
|
|
29
29
|
|
30
30
|
def initialize(options = {})
|
31
31
|
self.receiver = options.delete(:receiver) || self.class.receiver.new
|
32
|
-
receiver.data.
|
33
|
-
receiver.data.merge(options)
|
32
|
+
receiver.data.default.merge!(self.class.default_options)
|
33
|
+
receiver.data.merge!(options)
|
34
34
|
end
|
35
35
|
|
36
36
|
def after
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ccp::Persistent
|
2
|
+
NotFound = Class.new(RuntimeError)
|
3
|
+
|
4
|
+
autoload :Base , 'ccp/persistent/base'
|
5
|
+
autoload :Dir , 'ccp/persistent/dir'
|
6
|
+
autoload :File , 'ccp/persistent/file'
|
7
|
+
autoload :Json , 'ccp/persistent/json'
|
8
|
+
autoload :Tsv , 'ccp/persistent/tsv'
|
9
|
+
autoload :Versioned, 'ccp/persistent/versioned'
|
10
|
+
|
11
|
+
def self.lookup(name)
|
12
|
+
case name.to_s
|
13
|
+
when "dir" ; Ccp::Persistent::Dir
|
14
|
+
when "tsv" ; Ccp::Persistent::Tsv
|
15
|
+
when "file" ; Ccp::Persistent::File
|
16
|
+
when "json" ; Ccp::Persistent::Json
|
17
|
+
else
|
18
|
+
name.must(Ccp::Persistent::Base) {
|
19
|
+
raise NotFound, "%s: %s" % [name.class, name]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Ccp::Persistent::Base
|
4
|
+
attr_reader :source
|
5
|
+
attr_reader :serializer
|
6
|
+
|
7
|
+
delegate :ext, :encode, :decode, :to => :serializer
|
8
|
+
|
9
|
+
def initialize(source, serializer)
|
10
|
+
@source = source
|
11
|
+
@serializer = Ccp::Serializers.lookup(serializer)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.ext
|
15
|
+
raise NotImplementedError, "subclass resposibility"
|
16
|
+
end
|
17
|
+
|
18
|
+
def exist?(key)
|
19
|
+
raise NotImplementedError, "subclass resposibility"
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(hash)
|
23
|
+
hash.keys.each do |key|
|
24
|
+
self[key] = hash[key]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load!(key)
|
29
|
+
raise NotImplementedError, "subclass resposibility"
|
30
|
+
end
|
31
|
+
|
32
|
+
def load(key)
|
33
|
+
raise NotImplementedError, "subclass resposibility"
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](key)
|
37
|
+
load!(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def []=(key, val)
|
41
|
+
raise NotImplementedError, "subclass resposibility"
|
42
|
+
end
|
43
|
+
|
44
|
+
def keys
|
45
|
+
raise NotImplementedError, "subclass resposibility"
|
46
|
+
end
|
47
|
+
|
48
|
+
def truncate
|
49
|
+
raise NotImplementedError, "subclass resposibility"
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Ccp::Persistent::Dir < Ccp::Persistent::Base
|
4
|
+
def self.ext
|
5
|
+
""
|
6
|
+
end
|
7
|
+
|
8
|
+
def exist?(key)
|
9
|
+
path_for(key).exist?
|
10
|
+
end
|
11
|
+
|
12
|
+
def load!(key)
|
13
|
+
path = path_for(key)
|
14
|
+
if path.exist?
|
15
|
+
decode(path.read{})
|
16
|
+
else
|
17
|
+
raise Ccp::Persistent::NotFound, key.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def load(key)
|
22
|
+
load!(key)
|
23
|
+
rescue Ccp::Persistent::NotFound
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def []=(key, val)
|
28
|
+
path_for(key).open("w+"){|f| f.print encode(val)}
|
29
|
+
end
|
30
|
+
|
31
|
+
def keys
|
32
|
+
Dir["#{path}/*.#{ext}"].map{|i| File.basename(i, ".*")}.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
def truncate
|
36
|
+
Dir["#{path}/*.#{ext}"].each{|file| File.unlink(file)}
|
37
|
+
end
|
38
|
+
|
39
|
+
def path
|
40
|
+
@path ||= Pathname(@source)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def path_for(key, mkdir = true)
|
45
|
+
path.mkpath if mkdir
|
46
|
+
path + "#{key}.#{ext}"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Ccp::Persistent::File < Ccp::Persistent::Base
|
4
|
+
def self.ext
|
5
|
+
""
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(source, serializer)
|
9
|
+
@serializer = Ccp::Serializers.lookup(serializer)
|
10
|
+
@source = File.extname(source) == ".#{ext}" ? source : "#{source}.#{ext}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def exist?(key)
|
14
|
+
read_data.has_key?(key.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def load!(key)
|
18
|
+
hash = read_data
|
19
|
+
if hash.has_key?(key.to_s)
|
20
|
+
read_data[key.to_s]
|
21
|
+
else
|
22
|
+
raise Ccp::Persistent::NotFound, key.to_s
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load(key)
|
27
|
+
load!(key)
|
28
|
+
rescue Ccp::Persistent::NotFound
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def []=(key, val)
|
33
|
+
hash = read_data
|
34
|
+
hash[key.to_s] = val
|
35
|
+
raw_write(encode(hash))
|
36
|
+
end
|
37
|
+
|
38
|
+
def keys
|
39
|
+
read_data.keys.sort
|
40
|
+
end
|
41
|
+
|
42
|
+
def truncate
|
43
|
+
File.unlink(path.to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
def path
|
47
|
+
@path ||= Pathname(@source)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def read_data
|
52
|
+
path.exist? ? decode(path.read{}).must(Hash) : {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def raw_write(buf)
|
56
|
+
path.parent.mkpath
|
57
|
+
path.open("w+"){|f| f.print buf}
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Ccp::Persistent::Tsv < Ccp::Persistent::Dir
|
4
|
+
def self.ext
|
5
|
+
"tsv"
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_tsv(path)
|
9
|
+
hash = {}
|
10
|
+
path.readlines.each_with_index do |line, i|
|
11
|
+
no = i+1
|
12
|
+
key, val = line.split(/\t/,2)
|
13
|
+
unless val
|
14
|
+
$stderr.puts "#{self.class}#load_tsv: value not found. key='#{key}' (line: #{no})"
|
15
|
+
next
|
16
|
+
end
|
17
|
+
obj = decode(val)
|
18
|
+
hash[key] = obj
|
19
|
+
end
|
20
|
+
|
21
|
+
return hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def save_tsv(f, hash)
|
25
|
+
keys = hash.keys
|
26
|
+
keys =
|
27
|
+
case keys.first
|
28
|
+
when NilClass ; return
|
29
|
+
when Symbol ; keys
|
30
|
+
when /\A\d+\Z/; keys.sort_by(&:to_i)
|
31
|
+
when String ; keys.sort
|
32
|
+
else ; keys
|
33
|
+
end
|
34
|
+
|
35
|
+
keys.each do |key|
|
36
|
+
f.puts "%s\t%s\n" % [key, encode(hash[key])]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def load!(key)
|
41
|
+
path = path_for(key)
|
42
|
+
if path.exist?
|
43
|
+
super
|
44
|
+
elsif (tsv = tsv_path_for(key)).exist?
|
45
|
+
load_tsv(tsv)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def []=(key, val)
|
52
|
+
# Now, tsv can manage only hash
|
53
|
+
case val
|
54
|
+
when Hash
|
55
|
+
tsv_path_for(key).open("w+"){|f| save_tsv(f, val)}
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def files
|
62
|
+
Dir["#{path}/*.#{ext}"] + Dir["#{path}/*.#{ext}.#{self.class.ext}"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def keys
|
66
|
+
files.map{|i| File.basename(i).split(".").first}.sort
|
67
|
+
end
|
68
|
+
|
69
|
+
def truncate
|
70
|
+
files.each{|file| File.unlink(file)}
|
71
|
+
end
|
72
|
+
|
73
|
+
def tsv_path_for(key)
|
74
|
+
Pathname(path_for(key).to_s + ".tsv")
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Ccp::Persistent::Versioned
|
4
|
+
attr_reader :path
|
5
|
+
|
6
|
+
Storage = Struct.new(:dir, :name, :ext, :kvs)
|
7
|
+
class Storage
|
8
|
+
def self.complete(file, default_dir, default_kvs, default_ext)
|
9
|
+
s = file.must(Storage) {
|
10
|
+
path, ext, kvs = file.to_s.split(".", 3)
|
11
|
+
new(default_dir, path, ext, kvs)
|
12
|
+
}
|
13
|
+
|
14
|
+
s.name = Pathname(s.name).basename(".*").to_s
|
15
|
+
s.dir = default_dir if s.dir.blank?
|
16
|
+
s.ext = default_ext if s.ext.blank?
|
17
|
+
s.kvs = default_kvs if s.kvs.blank?
|
18
|
+
|
19
|
+
s.ext.must.not.blank
|
20
|
+
s.kvs.must.not.blank
|
21
|
+
|
22
|
+
return s
|
23
|
+
end
|
24
|
+
|
25
|
+
def path
|
26
|
+
kvs = Ccp::Persistent.lookup(self.kvs)
|
27
|
+
ext = Ccp::Serializers.lookup(self.ext)
|
28
|
+
base = [name.to_s, ext.ext, kvs.ext].join(".").sub(/\.$/,'')
|
29
|
+
return Pathname(dir) + base
|
30
|
+
end
|
31
|
+
|
32
|
+
def create
|
33
|
+
kvs = Ccp::Persistent.lookup(self.kvs)
|
34
|
+
ext = Ccp::Serializers.lookup(self.ext)
|
35
|
+
kvs.new(path, ext)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module StorageScanner
|
40
|
+
def scan(dir)
|
41
|
+
files = Dir.chdir(dir){
|
42
|
+
Dir["*"].grep(/^(\d+)\./).sort{|a,b|
|
43
|
+
[a.to_i, File.mtime(a)] <=> [b.to_i, File.mtime(b)]
|
44
|
+
}
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
extend self
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(dir, options = {})
|
52
|
+
@path = Pathname(dir)
|
53
|
+
@default_kvs = options[:kvs] || :dir
|
54
|
+
@default_ext = options[:ext] || :json
|
55
|
+
@storages = {}
|
56
|
+
|
57
|
+
@path.mkpath
|
58
|
+
end
|
59
|
+
|
60
|
+
# 最新のストレージを返す。存在しなければnil
|
61
|
+
def latest
|
62
|
+
storage = StorageScanner.scan(path).last
|
63
|
+
storage ? self[storage] : nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# 最新のストレージを返す。存在しなければ例外
|
67
|
+
def latest!
|
68
|
+
latest.must.exist { raise Ccp::Persistent::NotFound, "#{path}/*" }
|
69
|
+
end
|
70
|
+
|
71
|
+
# 最新のストレージを返す。存在しなければ作成
|
72
|
+
def default
|
73
|
+
latest || now
|
74
|
+
end
|
75
|
+
|
76
|
+
# 現在の時刻で新しいストレージを作成して返す
|
77
|
+
def now
|
78
|
+
self[Time.now.to_i]
|
79
|
+
end
|
80
|
+
|
81
|
+
# 指定したストレージを返す。存在しなければ作成して返す
|
82
|
+
def [](key)
|
83
|
+
storage = Storage.complete(key, path, @default_kvs, @default_ext)
|
84
|
+
@storages[storage.to_s] ||= storage.create
|
85
|
+
end
|
86
|
+
|
87
|
+
def inspect
|
88
|
+
"<Kvs::Versioned dir=#{path} kvs=#{@default_kvs} ext=#{@default_ext}>"
|
89
|
+
end
|
90
|
+
end
|
data/lib/ccp/receivers.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
module Ccp
|
2
2
|
module Receivers
|
3
|
+
autoload :Core , 'ccp/receivers/core'
|
3
4
|
autoload :Base , 'ccp/receivers/base'
|
4
5
|
autoload :Global , 'ccp/receivers/global'
|
6
|
+
autoload :Settings , 'ccp/receivers/settings'
|
7
|
+
autoload :Variables , 'ccp/receivers/variables'
|
5
8
|
autoload :Executable , 'ccp/receivers/executable'
|
6
9
|
autoload :Aroundable , 'ccp/receivers/aroundable'
|
7
10
|
autoload :Commentable , 'ccp/receivers/commentable'
|
8
11
|
autoload :Profileable , 'ccp/receivers/profileable'
|
9
|
-
autoload :
|
12
|
+
autoload :Fixtures , 'ccp/receivers/fixtures'
|
10
13
|
end
|
11
14
|
end
|
data/lib/ccp/receivers/base.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
module Ccp
|
2
2
|
module Receivers
|
3
3
|
class Base
|
4
|
-
include
|
4
|
+
include Core
|
5
|
+
include Settings
|
6
|
+
include Variables
|
5
7
|
include Commentable
|
6
8
|
|
7
9
|
# for execute
|
8
10
|
include Executable
|
9
11
|
include Profileable
|
10
12
|
include Aroundable
|
11
|
-
include
|
13
|
+
include Fixtures
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
"#<Receivers::#{klass_name} data:#{data.inspect}>"
|
17
|
-
end
|
15
|
+
# ensure to call '#setup' for module initializations
|
16
|
+
def self.new(*args) r = super; r.setup; r; end
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|