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.
Files changed (45) hide show
  1. data/ccp.gemspec +1 -1
  2. data/lib/ccp.rb +4 -2
  3. data/lib/ccp/invokers/base.rb +2 -2
  4. data/lib/ccp/persistent.rb +23 -0
  5. data/lib/ccp/persistent/base.rb +51 -0
  6. data/lib/ccp/persistent/dir.rb +48 -0
  7. data/lib/ccp/persistent/file.rb +59 -0
  8. data/lib/ccp/persistent/tsv.rb +76 -0
  9. data/lib/ccp/persistent/versioned.rb +90 -0
  10. data/lib/ccp/receivers.rb +4 -1
  11. data/lib/ccp/receivers/base.rb +6 -7
  12. data/lib/ccp/receivers/commentable.rb +1 -1
  13. data/lib/ccp/receivers/core.rb +14 -0
  14. data/lib/ccp/receivers/fixtures.rb +75 -0
  15. data/lib/ccp/receivers/profileable.rb +1 -1
  16. data/lib/ccp/receivers/settings.rb +21 -0
  17. data/lib/ccp/receivers/variables.rb +21 -0
  18. data/lib/ccp/serializers.rb +19 -0
  19. data/lib/ccp/serializers/core.rb +13 -0
  20. data/lib/ccp/serializers/json.rb +15 -0
  21. data/lib/ccp/serializers/yaml.rb +15 -0
  22. data/lib/ccp/utils.rb +6 -0
  23. data/lib/ccp/utils/colorize.rb +15 -0
  24. data/lib/ccp/version.rb +1 -1
  25. data/spec/{commands_base_spec.rb → commands/base_spec.rb} +0 -0
  26. data/spec/{commands_composite_spec.rb → commands/composite_spec.rb} +0 -0
  27. data/spec/{commands_core_spec.rb → commands/core_spec.rb} +0 -0
  28. data/spec/{commands_executable_spec.rb → commands/executable_spec.rb} +0 -0
  29. data/spec/{invokers_spec.rb → invokers/base_spec.rb} +0 -0
  30. data/spec/persistent/base_spec.rb +49 -0
  31. data/spec/persistent/dir_spec.rb +111 -0
  32. data/spec/persistent/file_spec.rb +124 -0
  33. data/spec/persistent/tsv_spec.rb +19 -0
  34. data/spec/persistent/versioned_spec.rb +110 -0
  35. data/spec/receivers/fixture_save_spec.rb +79 -0
  36. data/spec/serializers/core_spec.rb +19 -0
  37. data/spec/serializers/json_spec.rb +27 -0
  38. data/spec/serializers/spec.rb +39 -0
  39. data/spec/serializers/yaml_spec.rb +27 -0
  40. metadata +39 -18
  41. data/lib/ccp/colorize.rb +0 -13
  42. data/lib/ccp/data.rb +0 -37
  43. data/lib/ccp/receivers/save_fixture.rb +0 -45
  44. data/spec/data_spec.rb +0 -18
  45. 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.5"
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 :Colorize, 'ccp/colorize'
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
@@ -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.merge_default(self.class.default_options)
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 :SaveFixture , 'ccp/receivers/save_fixture'
12
+ autoload :Fixtures , 'ccp/receivers/fixtures'
10
13
  end
11
14
  end
@@ -1,20 +1,19 @@
1
1
  module Ccp
2
2
  module Receivers
3
3
  class Base
4
- include Ccp::Data
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 SaveFixture
13
+ include Fixtures
12
14
 
13
- def inspect
14
- klass_name = self.class.name.to_s.split(/::/).last
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
@@ -4,7 +4,7 @@ module Ccp
4
4
 
5
5
  Comment = Struct.new(:text, :level)
6
6
  class Comment
7
- include Colorize
7
+ include Utils::Colorize
8
8
  def colorized
9
9
  case level
10
10
  when :warn ; yellow(text)