ccp 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|