ccp 0.1.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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README +0 -0
- data/Rakefile +1 -0
- data/ccp.gemspec +26 -0
- data/lib/ccp.rb +11 -0
- data/lib/ccp/colorize.rb +13 -0
- data/lib/ccp/commands.rb +12 -0
- data/lib/ccp/commands/command_not_found.rb +6 -0
- data/lib/ccp/commands/commentable.rb +21 -0
- data/lib/ccp/commands/composite.rb +93 -0
- data/lib/ccp/commands/core.rb +14 -0
- data/lib/ccp/commands/executable.rb +17 -0
- data/lib/ccp/commands/receivable.rb +21 -0
- data/lib/ccp/commands/resolvable.rb +23 -0
- data/lib/ccp/commands/runtime_args.rb +24 -0
- data/lib/ccp/data.rb +107 -0
- data/lib/ccp/invokers.rb +5 -0
- data/lib/ccp/invokers/base.rb +62 -0
- data/lib/ccp/receivers.rb +8 -0
- data/lib/ccp/receivers/base.rb +15 -0
- data/lib/ccp/receivers/commentable.rb +35 -0
- data/lib/ccp/receivers/none.rb +9 -0
- data/lib/ccp/receivers/profileable.rb +57 -0
- data/lib/ccp/version.rb +3 -0
- data/spec/commands_spec.rb +18 -0
- data/spec/composite_spec.rb +85 -0
- data/spec/data_spec.rb +129 -0
- data/spec/spec_helper.rb +5 -0
- metadata +155 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/ccp.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ccp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ccp"
|
7
|
+
s.version = Ccp::VERSION
|
8
|
+
s.authors = ["maiha"]
|
9
|
+
s.email = ["maiha@wota.jp"]
|
10
|
+
s.homepage = "http://github.com/maiha/ccp"
|
11
|
+
s.summary = %q{A Ruby library for Composite Command Programming}
|
12
|
+
s.description = %q{CCP}
|
13
|
+
|
14
|
+
s.rubyforge_project = "ccp"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_dependency "activesupport"
|
22
|
+
s.add_dependency "must", ">= 0.2.5"
|
23
|
+
s.add_dependency "dsl_accessor", ">= 0.4.0"
|
24
|
+
|
25
|
+
s.add_development_dependency "rspec"
|
26
|
+
end
|
data/lib/ccp.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "must"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "ccp/version"
|
4
|
+
require "ccp/data"
|
5
|
+
|
6
|
+
module Ccp
|
7
|
+
autoload :Colorize, 'ccp/colorize'
|
8
|
+
autoload :Commands, 'ccp/commands'
|
9
|
+
autoload :Invokers, 'ccp/invokers'
|
10
|
+
autoload :Receivers, 'ccp/receivers'
|
11
|
+
end
|
data/lib/ccp/colorize.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Colorize
|
3
|
+
def colorize(text, ansi); "#{ansi}#{text}\e[0m"; end
|
4
|
+
def red (text); colorize(text, "\e[31m"); end
|
5
|
+
def green (text); colorize(text, "\e[32m"); end
|
6
|
+
def yellow(text); colorize(text, "\e[33m"); end
|
7
|
+
def blue (text); colorize(text, "\e[34m"); end
|
8
|
+
def pink (text); colorize(text, "\e[35m"); end
|
9
|
+
def aqua (text); colorize(text, "\e[36m"); end
|
10
|
+
|
11
|
+
extend self
|
12
|
+
end
|
13
|
+
end
|
data/lib/ccp/commands.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Commands
|
3
|
+
autoload :CommandNotFound, 'ccp/commands/command_not_found'
|
4
|
+
autoload :Composite , 'ccp/commands/composite'
|
5
|
+
autoload :Executable , 'ccp/commands/executable'
|
6
|
+
autoload :Receivable , 'ccp/commands/receivable'
|
7
|
+
autoload :RuntimeArgs , 'ccp/commands/runtime_args'
|
8
|
+
autoload :Commentable , 'ccp/commands/commentable'
|
9
|
+
autoload :Core , 'ccp/commands/core'
|
10
|
+
autoload :Resolvable , 'ccp/commands/resolvable'
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Commands
|
3
|
+
module Commentable
|
4
|
+
def MEMO(*messages)
|
5
|
+
from = caller.is_a?(Array) ? caller.first : self.class
|
6
|
+
receiver.comment("MEMO: #{_build_comment(*messages)}\n #{from}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def TODO(*messages)
|
10
|
+
from = caller.is_a?(Array) ? caller.first : self.class
|
11
|
+
receiver.comment("TODO: #{_build_comment(*messages)}\n #{from}", :warn)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def _build_comment(*objs)
|
16
|
+
objs = objs.first if objs.size == 1
|
17
|
+
objs.is_a?(String) ? objs : objs.inspect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Commands
|
3
|
+
module Composite
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
extend ClassMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
CommandClass = Struct.new(:klass, :args, :cond) unless defined?(CommandClass)
|
11
|
+
class CommandClass
|
12
|
+
def inspect
|
13
|
+
name = klass.name.split(/Commands::/,2).last
|
14
|
+
"<%s args=%s cond=%s>" % [name, args.inspect, cond.inspect]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
inspect
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
######################################################################
|
23
|
+
### Class Methods
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
include Resolvable
|
27
|
+
|
28
|
+
def prepend_command(klass, *args, &block)
|
29
|
+
commands.unshift CommandClass.new(resolve(klass), args, block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def append_command(klass, *args, &block)
|
33
|
+
klass = resolve(klass)
|
34
|
+
if commands.find{|cc| cc.klass == klass}
|
35
|
+
# ignore: already added
|
36
|
+
else
|
37
|
+
commands << CommandClass.new(klass, args, block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def command(*args)
|
42
|
+
append_command(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def commands
|
46
|
+
@commands ||= []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
######################################################################
|
51
|
+
### Instance Methods
|
52
|
+
|
53
|
+
include Core
|
54
|
+
include Enumerable
|
55
|
+
|
56
|
+
######################################################################
|
57
|
+
### Enumerable
|
58
|
+
|
59
|
+
def <<(command)
|
60
|
+
command = command.new if command.is_a?(Class) and command.ancestors.include?(Core)
|
61
|
+
command.must(Core)
|
62
|
+
commands << command
|
63
|
+
end
|
64
|
+
|
65
|
+
def commands
|
66
|
+
@commands ||= build_commands.must(Array)
|
67
|
+
end
|
68
|
+
|
69
|
+
######################################################################
|
70
|
+
### Commands
|
71
|
+
|
72
|
+
def execute
|
73
|
+
benchmark
|
74
|
+
end
|
75
|
+
|
76
|
+
def benchmark
|
77
|
+
pre if respond_to?(:pre)
|
78
|
+
commands.each(&:benchmark)
|
79
|
+
post if respond_to?(:post)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def build_commands
|
84
|
+
array = self.class.commands.select{|c| c.cond.nil? or instance_eval(&c.cond)}
|
85
|
+
array.map{|c|
|
86
|
+
c = c.klass.new(*c.args)
|
87
|
+
c.receiver = receiver
|
88
|
+
c
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Commands
|
3
|
+
module Receivable
|
4
|
+
######################################################################
|
5
|
+
### Receiver Methods
|
6
|
+
|
7
|
+
def receiver
|
8
|
+
@receiver ||= Ccp::Receivers::None.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def receiver=(value)
|
12
|
+
@receiver = value.must(Ccp::Receivers::Base)
|
13
|
+
end
|
14
|
+
|
15
|
+
######################################################################
|
16
|
+
### Delegate Methods
|
17
|
+
|
18
|
+
delegate :data, :data?, :to=>"receiver"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Commands
|
3
|
+
module Resolvable
|
4
|
+
def resolve(klass)
|
5
|
+
klass.must.coerced(Class, Module) {
|
6
|
+
raise CommandNotFound, "expected Class or Module, but got #{klass.class}"
|
7
|
+
}
|
8
|
+
|
9
|
+
if klass.ancestors.include?(Commands::Core)
|
10
|
+
return klass # ok
|
11
|
+
end
|
12
|
+
|
13
|
+
if klass.must.duck?("#execute")
|
14
|
+
# dynamically assign core
|
15
|
+
klass.class_eval{ include Commands::Core }
|
16
|
+
return klass
|
17
|
+
end
|
18
|
+
|
19
|
+
raise CommandNotFound, "#{klass} found but it misses 'execute' method"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# map options to data
|
3
|
+
|
4
|
+
module Ccp
|
5
|
+
module Commands
|
6
|
+
class RuntimeArgs
|
7
|
+
include Core
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = (options || {}).must(Hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
@options.each_pair do |key, val|
|
15
|
+
data[key] = val
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
super.sub(/>$/, " #{@options.inspect}>")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/ccp/data.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Ccp
|
6
|
+
module Data
|
7
|
+
NotDefined = Class.new(RuntimeError)
|
8
|
+
|
9
|
+
class KVS
|
10
|
+
delegate :[]=, :to=>"@hash"
|
11
|
+
|
12
|
+
LazyValue = Struct.new(:block)
|
13
|
+
|
14
|
+
class Default
|
15
|
+
def initialize(kvs)
|
16
|
+
@kvs = kvs
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, val)
|
20
|
+
return if @kvs.exist?(key)
|
21
|
+
@kvs[key] = val
|
22
|
+
end
|
23
|
+
|
24
|
+
def regsiter_lazy(key, block)
|
25
|
+
return if @kvs.exist?(key)
|
26
|
+
raise ArgumentError, "Lazy default value needs block: #{key}" unless block
|
27
|
+
@kvs[key] = LazyValue.new(block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@hash = {}
|
33
|
+
@default = Default.new(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
######################################################################
|
37
|
+
### Default values
|
38
|
+
|
39
|
+
def default(key = nil, &block)
|
40
|
+
if key
|
41
|
+
@default.regsiter_lazy(key, block)
|
42
|
+
else
|
43
|
+
@default
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
######################################################################
|
48
|
+
### Accessor
|
49
|
+
|
50
|
+
def [](key)
|
51
|
+
if exist?(key)
|
52
|
+
return load(key)
|
53
|
+
else
|
54
|
+
from = caller.is_a?(Array) ? caller.first : self.class
|
55
|
+
raise NotDefined, "'#{key}' is not initialized\n#{from}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
######################################################################
|
60
|
+
### Testing
|
61
|
+
|
62
|
+
def exist?(key)
|
63
|
+
@hash.has_key?(key)
|
64
|
+
end
|
65
|
+
|
66
|
+
def set?(key)
|
67
|
+
!! (exist?(key) && self[key])
|
68
|
+
end
|
69
|
+
|
70
|
+
def check(key, type)
|
71
|
+
self[key].must.struct(type) {
|
72
|
+
summary = self[key].inspect
|
73
|
+
summary = summary[0,200] + "..." if summary.size > 200
|
74
|
+
raise TypeError, "'#{key}' expects '#{type.inspect}', but got #{summary}"
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
######################################################################
|
79
|
+
### Utils
|
80
|
+
|
81
|
+
def path(key)
|
82
|
+
self[key].must.coerced(Pathname, String=>proc{|i| Pathname(i)})
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
keys = @hash.keys.map(&:to_s).sort.join(',')
|
87
|
+
"{#{keys}}"
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def load(key)
|
92
|
+
# LazyValue should be evaluated at runtime
|
93
|
+
value = @hash[key]
|
94
|
+
value = value.block.call if value.is_a?(LazyValue)
|
95
|
+
return value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def data
|
100
|
+
@data ||= KVS.new
|
101
|
+
end
|
102
|
+
|
103
|
+
def data?(key)
|
104
|
+
data.set?(key)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/ccp/invokers.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Ccp
|
4
|
+
module Invokers
|
5
|
+
class Base
|
6
|
+
include Commands::Composite
|
7
|
+
|
8
|
+
dsl_accessor :receiver, Receivers::Base
|
9
|
+
delegate :show_profiles, :profiles, :to=>"receiver"
|
10
|
+
delegate :show_comments, :comments, :to=>"receiver"
|
11
|
+
|
12
|
+
DEFAULT_OPTIONS = {
|
13
|
+
:profile => false,
|
14
|
+
:comment => true,
|
15
|
+
}
|
16
|
+
|
17
|
+
######################################################################
|
18
|
+
### Instance Methods
|
19
|
+
|
20
|
+
def initialize(options = nil)
|
21
|
+
self.receiver = self.class.receiver.new
|
22
|
+
self.class.prepend_command Commands::RuntimeArgs, options
|
23
|
+
set_default_options
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_default_options
|
27
|
+
self.class::DEFAULT_OPTIONS.each_pair do |key,val|
|
28
|
+
data.default[key] = val
|
29
|
+
end
|
30
|
+
data.default(:logger) { Logger.new($stderr) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute
|
34
|
+
benchmark
|
35
|
+
end
|
36
|
+
|
37
|
+
def benchmark
|
38
|
+
before
|
39
|
+
super
|
40
|
+
after
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
|
44
|
+
def receiver
|
45
|
+
@receiver.must(Receivers::Base)
|
46
|
+
end
|
47
|
+
|
48
|
+
def receiver=(value)
|
49
|
+
@receiver = value.must(Receivers::Base)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def before
|
54
|
+
end
|
55
|
+
|
56
|
+
def after
|
57
|
+
show_profiles if data?(:profile)
|
58
|
+
show_comments if data?(:comment)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Receivers
|
3
|
+
class Base
|
4
|
+
include Ccp::Data
|
5
|
+
include Profileable
|
6
|
+
include Commentable
|
7
|
+
|
8
|
+
def inspect
|
9
|
+
klass_name = self.class.name.to_s.split(/::/).last
|
10
|
+
|
11
|
+
"#<Receivers::#{klass_name} data:#{data.inspect}>"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Receivers
|
3
|
+
module Commentable
|
4
|
+
|
5
|
+
Comment = Struct.new(:text, :level)
|
6
|
+
class Comment
|
7
|
+
include Colorize
|
8
|
+
def colorized
|
9
|
+
case level
|
10
|
+
when :warn ; yellow(text)
|
11
|
+
when :error, :fatal ; red(text)
|
12
|
+
when :info ; green(text)
|
13
|
+
else ; text
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def comment(text, level = nil)
|
19
|
+
comments[text] ||= Comment.new(text, level)
|
20
|
+
end
|
21
|
+
|
22
|
+
def comments
|
23
|
+
@comments ||= ActiveSupport::OrderedHash.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def show_comments(*args)
|
27
|
+
opts = Optionize.new(args, :output)
|
28
|
+
output = opts[:output] || $stderr
|
29
|
+
comments.each_value do |comment|
|
30
|
+
output.puts comment.colorized
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Ccp
|
2
|
+
module Receivers
|
3
|
+
module Profileable
|
4
|
+
include Colorize
|
5
|
+
|
6
|
+
Profile = Struct.new(:target, :method, :time)
|
7
|
+
class Profile
|
8
|
+
def name
|
9
|
+
target.is_a?(Class) ? "#{target.name}.#{method}" : "#{target.class}##{method}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def profile(total)
|
13
|
+
"[%4.1f%%] %.7f %s" % [(time*100/total), time, name]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def profile(target, method)
|
18
|
+
start = Time.new
|
19
|
+
target.pre if target.must.duck?(:pre)
|
20
|
+
target.__send__(method)
|
21
|
+
target.post if target.must.duck?(:post)
|
22
|
+
target.must.duck(:no_bench) {
|
23
|
+
profiles << Profile.new(target, method, (Time.new - start).to_f)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def profiles
|
28
|
+
@profiles ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def show_profiles(*args, &block)
|
32
|
+
opts = Optionize.new(args, :benchs, :output)
|
33
|
+
benchs = opts[:benchs] || profiles
|
34
|
+
output = opts[:output] || $stderr
|
35
|
+
|
36
|
+
# search worst item
|
37
|
+
total = 0
|
38
|
+
worst = nil
|
39
|
+
benchs.each do |bench|
|
40
|
+
total += bench.time
|
41
|
+
worst = bench if !worst or bench.time > worst.time
|
42
|
+
end
|
43
|
+
|
44
|
+
benchs.each do |bench|
|
45
|
+
colorize = (bench == worst) ? :pink : :aqua
|
46
|
+
profiled = __send__(colorize, bench.profile(total))
|
47
|
+
if block
|
48
|
+
block.call(profiled)
|
49
|
+
else
|
50
|
+
output.puts profiled
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/ccp/version.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ccp::Commands::Core do
|
4
|
+
subject { Object.new.extend Ccp::Commands::Core }
|
5
|
+
|
6
|
+
# data container
|
7
|
+
it { should respond_to(:data?) }
|
8
|
+
it { should respond_to(:data) }
|
9
|
+
its(:data) { should be_kind_of(Ccp::Data::KVS) }
|
10
|
+
|
11
|
+
# executable
|
12
|
+
it { should respond_to(:execute) }
|
13
|
+
it { should respond_to(:benchmark) }
|
14
|
+
|
15
|
+
# receivable
|
16
|
+
it { should respond_to(:receiver) }
|
17
|
+
its(:receiver) { should be_kind_of(Ccp::Receivers::Base) }
|
18
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ccp::Commands::Composite do
|
4
|
+
module Breadcrumbing
|
5
|
+
BREADCRUMBS = []
|
6
|
+
|
7
|
+
def this
|
8
|
+
"%s#%s" % [self.class.name, caller[0].to_s.scan(/`(.*?)'/).first]
|
9
|
+
end
|
10
|
+
|
11
|
+
def breadcrumbs
|
12
|
+
BREADCRUMBS
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Cmd1
|
17
|
+
include Ccp::Commands::Core
|
18
|
+
include Breadcrumbing
|
19
|
+
|
20
|
+
def execute
|
21
|
+
breadcrumbs << this
|
22
|
+
data[:foo] = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def post
|
26
|
+
breadcrumbs << this
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Cmd2
|
31
|
+
include Ccp::Commands::Core
|
32
|
+
include Breadcrumbing
|
33
|
+
|
34
|
+
def pre
|
35
|
+
breadcrumbs << this
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute
|
39
|
+
breadcrumbs << this
|
40
|
+
data[:foo] << "cmd2"
|
41
|
+
end
|
42
|
+
|
43
|
+
def post
|
44
|
+
breadcrumbs << this
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Cmd3
|
49
|
+
include Ccp::Commands::Core
|
50
|
+
include Breadcrumbing
|
51
|
+
|
52
|
+
def pre
|
53
|
+
breadcrumbs << this
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute
|
57
|
+
breadcrumbs << this
|
58
|
+
data[:foo] << "cmd3"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Program
|
63
|
+
include Ccp::Commands::Composite
|
64
|
+
|
65
|
+
command Cmd1
|
66
|
+
command Cmd2
|
67
|
+
command Cmd3
|
68
|
+
end
|
69
|
+
|
70
|
+
subject { Program.new }
|
71
|
+
|
72
|
+
it { should be_kind_of(Ccp::Commands::Core) }
|
73
|
+
|
74
|
+
it "should call sub commands's {pre,execute,post} in declared order" do
|
75
|
+
Breadcrumbing::BREADCRUMBS.clear
|
76
|
+
subject.execute
|
77
|
+
Breadcrumbing::BREADCRUMBS.should ==
|
78
|
+
["Cmd1#execute", "Cmd1#post", "Cmd2#pre", "Cmd2#execute", "Cmd2#post", "Cmd3#pre", "Cmd3#execute"]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should share same data storage in each sub commands" do
|
82
|
+
subject.execute
|
83
|
+
subject.data[:foo].should == ["cmd2", "cmd3"]
|
84
|
+
end
|
85
|
+
end
|
data/spec/data_spec.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ccp::Data::KVS do
|
4
|
+
######################################################################
|
5
|
+
### Accessor
|
6
|
+
|
7
|
+
it "should behave like hash" do
|
8
|
+
subject[:foo] = 1
|
9
|
+
subject[:foo].should == 1
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#[]" do
|
13
|
+
it "should raise NotDefined if no data exist for the key" do
|
14
|
+
lambda {
|
15
|
+
subject[:foo]
|
16
|
+
}.should raise_error(Ccp::Data::NotDefined)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#[]=" do
|
21
|
+
it "can override values" do
|
22
|
+
subject[:foo] = 1
|
23
|
+
subject[:foo] = 2
|
24
|
+
subject[:foo].should == 2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
######################################################################
|
29
|
+
### Testing
|
30
|
+
|
31
|
+
describe "#exist?" do
|
32
|
+
it "should return true if the value is set" do
|
33
|
+
subject[:foo] = 1
|
34
|
+
subject.exist?(:foo).should == true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return false if the value is not set" do
|
38
|
+
subject.exist?(:foo).should == false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#set?" do
|
43
|
+
it "should return true if the value is set and not (nil|false)" do
|
44
|
+
subject[:foo] = 0
|
45
|
+
subject.set?(:foo).should == true
|
46
|
+
|
47
|
+
subject.default[:bar] = 0
|
48
|
+
subject.set?(:bar).should == true
|
49
|
+
|
50
|
+
subject.default(:baz) { 0 }
|
51
|
+
subject.set?(:baz).should == true
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return false if the value is set but (nil|false)" do
|
55
|
+
subject[:foo] = nil
|
56
|
+
subject.set?(:foo).should == false
|
57
|
+
|
58
|
+
subject[:foo] = false
|
59
|
+
subject.set?(:foo).should == false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#check" do
|
64
|
+
it "should satisfy its type" do
|
65
|
+
subject[:foo] = 1
|
66
|
+
lambda {
|
67
|
+
subject.check(:foo, Integer)
|
68
|
+
}.should_not raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should satisfy its struct" do
|
72
|
+
subject[:foo] = {:a => "text"}
|
73
|
+
lambda {
|
74
|
+
subject.check(:foo, {Symbol => String})
|
75
|
+
}.should_not raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should raise TypeError if not satisfied" do
|
79
|
+
subject[:foo] = {:a => "text"}
|
80
|
+
lambda {
|
81
|
+
subject.check(:foo, Integer)
|
82
|
+
}.should raise_error(TypeError)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
######################################################################
|
87
|
+
### Default values
|
88
|
+
|
89
|
+
describe "#default" do
|
90
|
+
it "should set default value" do
|
91
|
+
subject.default[:foo] = 1
|
92
|
+
subject.exist?(:foo).should == true
|
93
|
+
subject[:foo].should == 1
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be overriden by []=" do
|
97
|
+
subject.default[:foo] = 1
|
98
|
+
subject[:foo] = 2
|
99
|
+
subject[:foo].should == 2
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should not affect data when already set" do
|
103
|
+
subject[:foo] = 1
|
104
|
+
subject.default[:foo] = 2
|
105
|
+
subject[:foo].should == 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "#default(&block)" do
|
110
|
+
it "should set lazy default value" do
|
111
|
+
@a = 1
|
112
|
+
subject.default(:foo) { @a }
|
113
|
+
@a = 2
|
114
|
+
subject[:foo].should == 2
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
######################################################################
|
119
|
+
### Utils
|
120
|
+
|
121
|
+
describe "#path" do
|
122
|
+
it "should return pathname" do
|
123
|
+
subject[:foo] = __FILE__
|
124
|
+
subject.path(:foo).should be_kind_of(Pathname)
|
125
|
+
subject.path(:foo).basename.should == Pathname("data_spec.rb")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ccp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- maiha
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-09 00:00:00 +09:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: activesupport
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: must
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 29
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 2
|
47
|
+
- 5
|
48
|
+
version: 0.2.5
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: dsl_accessor
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 15
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
- 4
|
63
|
+
- 0
|
64
|
+
version: 0.4.0
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rspec
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
description: CCP
|
82
|
+
email:
|
83
|
+
- maiha@wota.jp
|
84
|
+
executables: []
|
85
|
+
|
86
|
+
extensions: []
|
87
|
+
|
88
|
+
extra_rdoc_files: []
|
89
|
+
|
90
|
+
files:
|
91
|
+
- .gitignore
|
92
|
+
- Gemfile
|
93
|
+
- README
|
94
|
+
- Rakefile
|
95
|
+
- ccp.gemspec
|
96
|
+
- lib/ccp.rb
|
97
|
+
- lib/ccp/colorize.rb
|
98
|
+
- lib/ccp/commands.rb
|
99
|
+
- lib/ccp/commands/command_not_found.rb
|
100
|
+
- lib/ccp/commands/commentable.rb
|
101
|
+
- lib/ccp/commands/composite.rb
|
102
|
+
- lib/ccp/commands/core.rb
|
103
|
+
- lib/ccp/commands/executable.rb
|
104
|
+
- lib/ccp/commands/receivable.rb
|
105
|
+
- lib/ccp/commands/resolvable.rb
|
106
|
+
- lib/ccp/commands/runtime_args.rb
|
107
|
+
- lib/ccp/data.rb
|
108
|
+
- lib/ccp/invokers.rb
|
109
|
+
- lib/ccp/invokers/base.rb
|
110
|
+
- lib/ccp/receivers.rb
|
111
|
+
- lib/ccp/receivers/base.rb
|
112
|
+
- lib/ccp/receivers/commentable.rb
|
113
|
+
- lib/ccp/receivers/none.rb
|
114
|
+
- lib/ccp/receivers/profileable.rb
|
115
|
+
- lib/ccp/version.rb
|
116
|
+
- spec/commands_spec.rb
|
117
|
+
- spec/composite_spec.rb
|
118
|
+
- spec/data_spec.rb
|
119
|
+
- spec/spec_helper.rb
|
120
|
+
has_rdoc: true
|
121
|
+
homepage: http://github.com/maiha/ccp
|
122
|
+
licenses: []
|
123
|
+
|
124
|
+
post_install_message:
|
125
|
+
rdoc_options: []
|
126
|
+
|
127
|
+
require_paths:
|
128
|
+
- lib
|
129
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
hash: 3
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
version: "0"
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
144
|
+
segments:
|
145
|
+
- 0
|
146
|
+
version: "0"
|
147
|
+
requirements: []
|
148
|
+
|
149
|
+
rubyforge_project: ccp
|
150
|
+
rubygems_version: 1.3.7
|
151
|
+
signing_key:
|
152
|
+
specification_version: 3
|
153
|
+
summary: A Ruby library for Composite Command Programming
|
154
|
+
test_files: []
|
155
|
+
|