cuttlebone 0.1.3
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 +6 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +2 -0
- data/README.md +56 -0
- data/Rakefile +55 -0
- data/cuttlebone.gemspec +27 -0
- data/examples/todo.rb +57 -0
- data/features/example.feature +53 -0
- data/features/step_definitions/steps.rb +76 -0
- data/features/support/env.rb +2 -0
- data/features/support/test_driver.rb +24 -0
- data/features/switching_contexts.feature +78 -0
- data/lib/cuttlebone.rb +19 -0
- data/lib/cuttlebone/controller.rb +69 -0
- data/lib/cuttlebone/definition.rb +58 -0
- data/lib/cuttlebone/exceptions.rb +24 -0
- data/lib/cuttlebone/session.rb +4 -0
- data/lib/cuttlebone/session/base.rb +61 -0
- data/lib/cuttlebone/session/rack.rb +4 -0
- data/lib/cuttlebone/session/shell.rb +17 -0
- data/lib/cuttlebone/version.rb +3 -0
- data/spec/controller_spec.rb +79 -0
- data/spec/cuttlebone_spec.rb +19 -0
- data/spec/definition_spec.rb +100 -0
- data/spec/rcov.opts +2 -0
- data/spec/session_base_spec.rb +82 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +8 -0
- data/vendor/active_support.rb +56 -0
- metadata +142 -0
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
## 0.1.2 / 2011-03-18
|
2
|
+
|
3
|
+
* Changed directory structure to look like a real gem. :)
|
4
|
+
* Finalized initial test cases (spec/cucumber).
|
5
|
+
* github.com release!
|
6
|
+
* Added my original todo example using the new syntax.
|
7
|
+
|
8
|
+
## 0.1.1 / 2011-03-15
|
9
|
+
|
10
|
+
* Added new test cases and new "polished" features.
|
11
|
+
|
12
|
+
## 0.1.0 / 2010-07-06
|
13
|
+
|
14
|
+
* After a long (inactive) year, some projects generated a need to push
|
15
|
+
cuttlebone a little further. There are no new features but tests and
|
16
|
+
documentation.
|
17
|
+
|
18
|
+
## 0.0.1 / 2009-05-13
|
19
|
+
|
20
|
+
* Birthday! Presentation on the topic (and my proof of concept demo):
|
21
|
+
http://www.viddler.com/explore/budapestrb/videos/4/
|
22
|
+
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# cuttlebone
|
2
|
+
|
3
|
+
Cuttlebone helps you creating shell-alike applications. Easily.
|
4
|
+
|
5
|
+
## INSTALL AND USAGE
|
6
|
+
|
7
|
+
Install Ruby 1.9.2, clone, setup, try. :) Check `examples` and `features`
|
8
|
+
directories for examples.
|
9
|
+
|
10
|
+
## TODO
|
11
|
+
|
12
|
+
* Documentation-documentation-documentation...
|
13
|
+
|
14
|
+
* Tests-tests-tests...
|
15
|
+
|
16
|
+
* Methods defined in context definitions should be called from command/prompt
|
17
|
+
blocks.
|
18
|
+
|
19
|
+
* For Symbol contexts special attribute and method definitions should work
|
20
|
+
intuitively.
|
21
|
+
|
22
|
+
* Terminal handling, colors, sizes (eg.
|
23
|
+
http://codeidol.com/other/rubyckbk/User-Interface/Determining-Terminal-Size/,
|
24
|
+
ncurses(?)).
|
25
|
+
|
26
|
+
* Autocomplete (eg. cldwalker/bond or with a new approach based on 1.9.2's
|
27
|
+
readline enhancement).
|
28
|
+
|
29
|
+
* Asynchronous (server-initiated) output.
|
30
|
+
|
31
|
+
* Web driver.
|
32
|
+
|
33
|
+
## LICENSE
|
34
|
+
|
35
|
+
(The MIT License)
|
36
|
+
|
37
|
+
Copyright (c) 2009-2011 Golda Bence <bence@golda.me>
|
38
|
+
|
39
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
40
|
+
a copy of this software and associated documentation files (the
|
41
|
+
'Software'), to deal in the Software without restriction, including
|
42
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
43
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
44
|
+
permit persons to whom the Software is furnished to do so, subject to
|
45
|
+
the following conditions:
|
46
|
+
|
47
|
+
The above copyright notice and this permission notice shall be
|
48
|
+
included in all copies or substantial portions of the Software.
|
49
|
+
|
50
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
51
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
52
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
53
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
54
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
55
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
56
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
require 'cucumber/rake/task'
|
8
|
+
|
9
|
+
require 'rake/packagetask'
|
10
|
+
require 'rake/gempackagetask'
|
11
|
+
|
12
|
+
CUTTLEBONE_GEMSPEC = eval(File.read(File.expand_path('../cuttlebone.gemspec', __FILE__)))
|
13
|
+
|
14
|
+
desc 'Default: run specs'
|
15
|
+
task :default => 'spec'
|
16
|
+
|
17
|
+
namespace :spec do
|
18
|
+
desc 'Run all specs in spec directory (format=progress)'
|
19
|
+
RSpec::Core::RakeTask.new(:progress) do |t|
|
20
|
+
t.pattern = './spec/**/*_spec.rb'
|
21
|
+
t.rspec_opts = ['--color', '--format=progress']
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Run all specs in spec directory (format=documentation)'
|
25
|
+
RSpec::Core::RakeTask.new(:documentation) do |t|
|
26
|
+
t.pattern = './spec/**/*_spec.rb'
|
27
|
+
t.rspec_opts = ['--color', '--format=documentation']
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "Run specs with rcov"
|
31
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
32
|
+
t.pattern = './spec/**/*_spec.rb'
|
33
|
+
t.rcov = true
|
34
|
+
t.rcov_opts = ['--exclude', 'gems/,spec/,features/']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
task :spec => 'spec:progress'
|
39
|
+
|
40
|
+
desc 'Run all cucumber tests.'
|
41
|
+
Cucumber::Rake::Task.new do |t|
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Generate documentation for the a4-core plugin.'
|
45
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
46
|
+
rdoc.rdoc_dir = 'rdoc'
|
47
|
+
rdoc.title = 'A4::Core'
|
48
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--charset=UTF-8'
|
49
|
+
rdoc.rdoc_files.include('README')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
52
|
+
|
53
|
+
Rake::GemPackageTask.new(CUTTLEBONE_GEMSPEC) do |p|
|
54
|
+
p.gem_spec = CUTTLEBONE_GEMSPEC
|
55
|
+
end
|
data/cuttlebone.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path("../lib/cuttlebone/version", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "cuttlebone"
|
5
|
+
s.version = Cuttlebone::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Bence Golda"]
|
8
|
+
s.email = ["bence@golda.me"]
|
9
|
+
s.homepage = "http://github.com/gbence/cuttlebone"
|
10
|
+
s.summary = "cuttlebone-#{Cuttlebone::VERSION}"
|
11
|
+
s.description = "Cuttlebone helps you creating shell-alike applications."
|
12
|
+
|
13
|
+
s.rubyforge_project = "cuttlebone"
|
14
|
+
s.required_rubygems_version = ">= 1.3.7"
|
15
|
+
|
16
|
+
s.add_development_dependency "bundler", "~> 1.0.0"
|
17
|
+
s.add_development_dependency "rspec", "~> 2.5.0"
|
18
|
+
s.add_development_dependency "i18n"
|
19
|
+
s.add_development_dependency "cucumber", "~> 0.10.0"
|
20
|
+
s.add_development_dependency "rcov", "~> 0.9.0"
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
|
24
|
+
s.extra_rdoc_files = [ "README.md" ]
|
25
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
26
|
+
s.require_path = 'lib'
|
27
|
+
end
|
data/examples/todo.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require File.expand_path('../../lib/cuttlebone', __FILE__)
|
5
|
+
|
6
|
+
##
|
7
|
+
# MODEL
|
8
|
+
|
9
|
+
class Task
|
10
|
+
@@id = 0
|
11
|
+
attr_accessor :id, :title
|
12
|
+
def initialize options={}
|
13
|
+
@id = (@@id+=1)
|
14
|
+
@title = options.delete(:title)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# CUTTLEBONE DESCRIPTIONS
|
20
|
+
|
21
|
+
context Array do
|
22
|
+
#def find_task_by_id(id_s)
|
23
|
+
# id = id_s.to_i
|
24
|
+
# detect { |t| t.id == id }
|
25
|
+
#end
|
26
|
+
|
27
|
+
prompt() { "tasks(#{size})" }
|
28
|
+
|
29
|
+
command(?l) { each { |t| output('(%03d) %50s' % [t.id, t.title]) } }
|
30
|
+
command(?n) { send(:<<, t = Task.new); add t }
|
31
|
+
command /^([0-9]+)$/ do |id_s|
|
32
|
+
#t = find_task_by_id(id_s)
|
33
|
+
id = id_s.to_i
|
34
|
+
t = detect { |t| t.id == id }
|
35
|
+
add(t) if t
|
36
|
+
end
|
37
|
+
command /^d ([0-9]+)$/ do |id_s|
|
38
|
+
#t = find_task_by_id(id_s)
|
39
|
+
id = id_s.to_i
|
40
|
+
t = detect { |t| t.id == id }
|
41
|
+
delete(t)
|
42
|
+
end
|
43
|
+
command(?q) { drop }
|
44
|
+
end
|
45
|
+
|
46
|
+
context Task do
|
47
|
+
prompt() { ('%03d' % [id]) + (' ' + (title.size<=20 ? title : "#{title[0..18]}…") rescue '') }
|
48
|
+
|
49
|
+
command(?q) { drop }
|
50
|
+
command /^(.+)$/ do |text|
|
51
|
+
send(:title=, text)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
at_exit do
|
56
|
+
Cuttlebone.run([Task.new(:title => 'x'), Task.new(:title => 'y')])
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
@console @example
|
2
|
+
Feature: a simple example
|
3
|
+
In order to be able to manage a simple todo list
|
4
|
+
As a programmer
|
5
|
+
I want to create cuttlebone program
|
6
|
+
|
7
|
+
Scenario: starting with no defined contexts
|
8
|
+
Given no cuttlebone code
|
9
|
+
When I start an "x" session
|
10
|
+
Then I should get an error
|
11
|
+
|
12
|
+
Scenario: starting with an existing context
|
13
|
+
Given the following cuttlebone code:
|
14
|
+
"""
|
15
|
+
context "x" do
|
16
|
+
end
|
17
|
+
"""
|
18
|
+
When I start an "x" session
|
19
|
+
Then I should see an empty prompt
|
20
|
+
And I should be in context "x"
|
21
|
+
|
22
|
+
Scenario: starting with a missing context
|
23
|
+
Given the following cuttlebone code:
|
24
|
+
"""
|
25
|
+
context "x" do
|
26
|
+
end
|
27
|
+
"""
|
28
|
+
When I start a "y" session
|
29
|
+
Then I should get an error
|
30
|
+
|
31
|
+
Scenario: starting a context with prompt defined
|
32
|
+
Given the following cuttlebone code:
|
33
|
+
"""
|
34
|
+
context "x" do
|
35
|
+
prompt { "prompt" }
|
36
|
+
end
|
37
|
+
"""
|
38
|
+
When I start an "x" session
|
39
|
+
Then I should see "prompt" as prompt
|
40
|
+
|
41
|
+
Scenario: invoking a simple command
|
42
|
+
Given the following cuttlebone code:
|
43
|
+
"""
|
44
|
+
context "x" do
|
45
|
+
command /^y$/ do
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
"""
|
50
|
+
When I start an "x" session
|
51
|
+
And I call command "y"
|
52
|
+
Then I should see an empty prompt
|
53
|
+
And I should be in context "x"
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'pp'
|
2
|
+
# schema / definitions
|
3
|
+
|
4
|
+
Given /^no cuttlebone code$/ do
|
5
|
+
Cuttlebone.definitions.clear
|
6
|
+
end
|
7
|
+
|
8
|
+
Given /^the following cuttlebone code:$/ do |string|
|
9
|
+
Given %{no cuttlebone code}
|
10
|
+
Cuttlebone.instance_eval(string)
|
11
|
+
end
|
12
|
+
|
13
|
+
# initialization
|
14
|
+
|
15
|
+
Given /^a started "([^"]*)" session$/ do |objects|
|
16
|
+
When %{I start a #{objects.inspect} session}
|
17
|
+
end
|
18
|
+
|
19
|
+
When /^I start (?:an? )?"([^"]*)" session$/ do |objects|
|
20
|
+
@d = Cuttlebone::Session::Test.new(*(objects.scan(/([^,]{1,})(?:,\s*)?/).flatten))
|
21
|
+
end
|
22
|
+
|
23
|
+
# invocation
|
24
|
+
|
25
|
+
When /^I call command "([^"]*)"$/ do |command|
|
26
|
+
@d.call(command)
|
27
|
+
end
|
28
|
+
|
29
|
+
# context related steps
|
30
|
+
|
31
|
+
Then /^I should be in context "([^"]*)"$/ do |name|
|
32
|
+
@d.active_context.context.to_s.should == name
|
33
|
+
end
|
34
|
+
|
35
|
+
Then /^I should see a terminated session$/ do
|
36
|
+
@d.should be_terminated
|
37
|
+
end
|
38
|
+
|
39
|
+
Then /^I should be in the same context$/ do
|
40
|
+
@d.previous_active_context.should == @d.active_context
|
41
|
+
end
|
42
|
+
|
43
|
+
Then /^I should not be in the same context$/ do
|
44
|
+
@d.previous_active_context.should_not == @d.active_context
|
45
|
+
end
|
46
|
+
|
47
|
+
# output related steps
|
48
|
+
|
49
|
+
Then /^I should see "([^"]*)"$/ do |text|
|
50
|
+
@d.output.should include(text)
|
51
|
+
end
|
52
|
+
|
53
|
+
# prompt related steps
|
54
|
+
|
55
|
+
Then /^I should see "([^"]*)" as prompt$/ do |text|
|
56
|
+
@d.prompt.should include(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
Then /^I should see \/([^\/]*)\/ as prompt$/ do |regexp|
|
60
|
+
@d.prompt.should match(regexp)
|
61
|
+
end
|
62
|
+
|
63
|
+
Then /^I should see an empty prompt$/ do
|
64
|
+
@d.prompt.should be_empty
|
65
|
+
end
|
66
|
+
|
67
|
+
# error related steps
|
68
|
+
|
69
|
+
Then /^I should see an error$/ do
|
70
|
+
@d.error.should_not be_blank
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^I should get an error$/ do
|
74
|
+
@d.internal_error.should_not be_blank
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Cuttlebone::Session::Test < Cuttlebone::Session::Base
|
2
|
+
def initialize *args
|
3
|
+
super
|
4
|
+
stack = []
|
5
|
+
@stack.each { |c| stack << c }
|
6
|
+
@stack_history = [ stack ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def process command
|
10
|
+
stack = []
|
11
|
+
@stack.each { |c| stack << c }
|
12
|
+
@stack_history << stack
|
13
|
+
a, n, o, e = super(command)
|
14
|
+
@output = o
|
15
|
+
@error = e
|
16
|
+
[ a, n, o, e ]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :output, :error
|
20
|
+
|
21
|
+
def previous_active_context
|
22
|
+
@stack_history[-2].last
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
Feature: switching between contexts
|
2
|
+
In order to be able to manage my todo list
|
3
|
+
As a programmer
|
4
|
+
I want to switch between cuttlebone contexts
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given the following cuttlebone code:
|
8
|
+
"""
|
9
|
+
context 'x' do
|
10
|
+
command 'q' do
|
11
|
+
drop
|
12
|
+
end
|
13
|
+
command 'y' do
|
14
|
+
replace 'y'
|
15
|
+
end
|
16
|
+
command 'yy' do
|
17
|
+
add 'y'
|
18
|
+
end
|
19
|
+
command 'x' do
|
20
|
+
self
|
21
|
+
end
|
22
|
+
command 'xx' do
|
23
|
+
add 'x'
|
24
|
+
end
|
25
|
+
command 'r' do
|
26
|
+
replace 'x'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'y' do
|
31
|
+
command 'q' do
|
32
|
+
drop
|
33
|
+
end
|
34
|
+
command 'y' do
|
35
|
+
self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
"""
|
39
|
+
|
40
|
+
Scenario: quitting
|
41
|
+
Given a started "x" session
|
42
|
+
When I call command "q"
|
43
|
+
Then I should see a terminated session
|
44
|
+
|
45
|
+
Scenario: replacing current context
|
46
|
+
Given a started "x" session
|
47
|
+
When I call command "y"
|
48
|
+
Then I should be in context "y"
|
49
|
+
|
50
|
+
Scenario: returning the same context
|
51
|
+
Given a started "x" session
|
52
|
+
When I call command "x"
|
53
|
+
Then I should be in the same context
|
54
|
+
|
55
|
+
Scenario: replacing with a similar context
|
56
|
+
Given a started "x" session
|
57
|
+
When I call command "r"
|
58
|
+
Then I should not be in the same context
|
59
|
+
|
60
|
+
Scenario: entering into a new context
|
61
|
+
Given a started "x" session
|
62
|
+
When I call command "yy"
|
63
|
+
Then I should be in context "y"
|
64
|
+
|
65
|
+
Scenario: dropping previously built context
|
66
|
+
Given a started "x" session
|
67
|
+
When I call command "yy"
|
68
|
+
And I call command "q"
|
69
|
+
Then I should be in context "x"
|
70
|
+
|
71
|
+
Scenario: consume multiple contexts and quit
|
72
|
+
Given a started "x,y,y,x" session
|
73
|
+
When I call command "q"
|
74
|
+
And I call command "q"
|
75
|
+
And I call command "q"
|
76
|
+
Then I should be in context "x"
|
77
|
+
When I call command "q"
|
78
|
+
Then I should see a terminated session
|
data/lib/cuttlebone.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$: << File.expand_path('../', __FILE__)
|
2
|
+
|
3
|
+
require File.expand_path('../../vendor/active_support.rb', __FILE__)
|
4
|
+
|
5
|
+
module Cuttlebone
|
6
|
+
require 'cuttlebone/exceptions'
|
7
|
+
autoload :Controller, 'cuttlebone/controller'
|
8
|
+
autoload :Definition, 'cuttlebone/definition'
|
9
|
+
autoload :Session, 'cuttlebone/session'
|
10
|
+
|
11
|
+
@@definitions = []
|
12
|
+
def self.definitions; @@definitions; end
|
13
|
+
|
14
|
+
def self.run starting_objects, default_driver=Session::Shell
|
15
|
+
default_driver.new(starting_objects).run
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
include Cuttlebone::Definition::DSL
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# NOTE: we don't really want to pollute this proxy with a lot of private
|
3
|
+
# (internal) methods to leave space for <<context>>'s real methods.
|
4
|
+
class Cuttlebone::Controller
|
5
|
+
|
6
|
+
attr_reader :session, :context, :definition
|
7
|
+
|
8
|
+
def initialize session, context
|
9
|
+
@session = session
|
10
|
+
@context = context
|
11
|
+
@definition = Cuttlebone.definitions.find { |d| d.match(context) }
|
12
|
+
raise Cuttlebone::InvalidContextError, context, "No definiton was found for #{context.inspect}!" unless @definition
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Processes a command on its context's domain.
|
17
|
+
#
|
18
|
+
def process command
|
19
|
+
@next_action = nil
|
20
|
+
@output = []
|
21
|
+
|
22
|
+
return([:self, self, [], nil]) if command.empty?
|
23
|
+
block, arguments = definition.proc_for(command)
|
24
|
+
|
25
|
+
instance_exec(*arguments, &block)
|
26
|
+
action, context = @next_action || [ :self, self ]
|
27
|
+
|
28
|
+
return([ action, context, @output, nil ])
|
29
|
+
rescue Cuttlebone::DoubleActionError
|
30
|
+
raise
|
31
|
+
rescue => e
|
32
|
+
return([ :self, self, [], %{Cuttlebone::Controller: #{e.message} (#{e.class})} ])
|
33
|
+
end
|
34
|
+
|
35
|
+
def prompt
|
36
|
+
return(instance_exec(&@definition.prompt) || '')
|
37
|
+
rescue => e
|
38
|
+
%{error: #{e.message} (#{e.class.name})}
|
39
|
+
end
|
40
|
+
|
41
|
+
def drop
|
42
|
+
__save_next_action! :drop
|
43
|
+
end
|
44
|
+
|
45
|
+
def add context
|
46
|
+
__save_next_action! :add, context
|
47
|
+
end
|
48
|
+
|
49
|
+
def replace context
|
50
|
+
__save_next_action! :replace, context
|
51
|
+
end
|
52
|
+
|
53
|
+
def output text
|
54
|
+
@output << text
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing method_name, *args, &block
|
58
|
+
return(@context.send(method_name, *args, &block)) if @context.respond_to?(method_name)
|
59
|
+
return(super)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def __save_next_action! action, context=nil
|
65
|
+
raise Cuttlebone::DoubleActionError if @next_action
|
66
|
+
@next_action = [ action, context ]
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Cuttlebone
|
2
|
+
class Definition
|
3
|
+
|
4
|
+
module DSL
|
5
|
+
def context object, options={}, &definition
|
6
|
+
Cuttlebone.definitions << Definition.new(object, options, &definition)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Parser
|
11
|
+
def initialize definition
|
12
|
+
@definition = definition
|
13
|
+
end
|
14
|
+
|
15
|
+
def command string_or_regexp, &block
|
16
|
+
@definition.commands << [ string_or_regexp, block, @last_description ]
|
17
|
+
@last_description = nil
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def description text
|
22
|
+
@last_description = text
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def prompt text='', &block
|
27
|
+
@definition.prompt = block_given? ? block : proc { text }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :prompt, :commands
|
32
|
+
|
33
|
+
def initialize object_or_class, options={}, &definition
|
34
|
+
raise ArgumentError, 'missing block' unless block_given?
|
35
|
+
@object_or_class = object_or_class
|
36
|
+
@options = options
|
37
|
+
@commands = [] #Array.new(proc { |c| [:self, c, '', nil] })
|
38
|
+
@prompt = proc { |c| '' }
|
39
|
+
|
40
|
+
@parser = Parser.new(self)
|
41
|
+
@parser.instance_eval(&definition)
|
42
|
+
end
|
43
|
+
|
44
|
+
delegate :command, :to => '@parser'
|
45
|
+
|
46
|
+
def match object
|
47
|
+
@object_or_class === object
|
48
|
+
# TODO :if :unless options
|
49
|
+
end
|
50
|
+
|
51
|
+
def proc_for command
|
52
|
+
string_or_regexp, block, description = commands.find { |(sr,b,d)| sr === command } || raise(UnknownCommandError, "Unknown command: #{command.inspect}!")
|
53
|
+
return([ block, $~.captures ]) if $~
|
54
|
+
return([ block, [] ])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cuttlebone
|
2
|
+
|
3
|
+
##
|
4
|
+
# Raised when Controller is invoked with an invalid (not matching) context.
|
5
|
+
#
|
6
|
+
class InvalidContextError < StandardError
|
7
|
+
attr_reader :context
|
8
|
+
def initialize context, *args, &block
|
9
|
+
@context = context
|
10
|
+
super *args, &block
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Raised on unknown command calls.
|
16
|
+
#
|
17
|
+
class UnknownCommandError < StandardError; end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Raised when multiple actions are invoked simultanously.
|
21
|
+
#
|
22
|
+
class DoubleActionError < StandardError; end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Cuttlebone::Session::Base
|
2
|
+
|
3
|
+
attr_reader :stack, :internal_error
|
4
|
+
|
5
|
+
def initialize *stack_objects
|
6
|
+
setup_contexts 'cuttlebone.stack_objects' => stack_objects
|
7
|
+
end
|
8
|
+
|
9
|
+
def active_context
|
10
|
+
stack.last
|
11
|
+
end
|
12
|
+
|
13
|
+
delegate :name, :prompt, :to => :active_context
|
14
|
+
|
15
|
+
def setup_contexts env={}
|
16
|
+
@stack ||= (env['cuttlebone.stack_objects'] || []).map{ |o| Cuttlebone::Controller.new(self, o) }
|
17
|
+
rescue Cuttlebone::InvalidContextError => e
|
18
|
+
@internal_error = %{Context initialization failed for #{e.context.inspect}!}
|
19
|
+
@stack = []
|
20
|
+
rescue => e
|
21
|
+
@internal_error = %{Internal error occured: #{e.message} (#{e.class})}
|
22
|
+
@stack = []
|
23
|
+
end
|
24
|
+
private :setup_contexts
|
25
|
+
|
26
|
+
def process command
|
27
|
+
active_context = @stack.pop
|
28
|
+
|
29
|
+
action, next_context, output, error = begin
|
30
|
+
a, n, o, e = active_context.process(command)
|
31
|
+
raise ArgumentError, "Unknown action: #{a}" unless [ :self, :replace, :add, :drop ].include?(a.to_s.to_sym)
|
32
|
+
raise TypeError, "Output must be an instance of String or nil!" unless o.is_a?(Array) or o.nil?
|
33
|
+
raise TypeError, "Error must be an instance of String or nil!" unless e.is_a?(String) or e.nil?
|
34
|
+
[ a.to_s.to_sym, n, o, e ]
|
35
|
+
rescue => e
|
36
|
+
[ :self, active_context, nil, %{Cuttlebone::Session: #{e.message} (#{e.class})} ]
|
37
|
+
end
|
38
|
+
case action
|
39
|
+
when :self
|
40
|
+
@stack << active_context
|
41
|
+
when :replace
|
42
|
+
@stack << Cuttlebone::Controller.new(self, next_context)
|
43
|
+
when :add
|
44
|
+
@stack << active_context << Cuttlebone::Controller.new(self, next_context)
|
45
|
+
when :drop
|
46
|
+
# noop
|
47
|
+
end
|
48
|
+
[ action, next_context, output, error ]
|
49
|
+
end
|
50
|
+
private :process
|
51
|
+
|
52
|
+
def call command, env={}
|
53
|
+
setup_contexts env
|
54
|
+
process command
|
55
|
+
end
|
56
|
+
|
57
|
+
def terminated?
|
58
|
+
stack.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# TODO FIXME load readline iff it was invoked through command line interface
|
2
|
+
require 'readline'
|
3
|
+
|
4
|
+
class Cuttlebone::Session::Shell < Cuttlebone::Session::Base
|
5
|
+
def run
|
6
|
+
loop do
|
7
|
+
break if terminated?
|
8
|
+
command = Readline::readline("#{prompt} > ")
|
9
|
+
break unless command
|
10
|
+
Readline::HISTORY.push(command)
|
11
|
+
_, _, output, error = call(command)
|
12
|
+
print (output<<'').join("\n")
|
13
|
+
print "\033[01;31m#{error}\033[00m\n" if error
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cuttlebone::Controller do
|
4
|
+
let(:valid_session) { mock("Cuttlebone::Session::Base") }
|
5
|
+
let(:valid_context) { 'x' }
|
6
|
+
|
7
|
+
let(:drop_proc) { proc { |*args| output 'dropped'; drop } }
|
8
|
+
let(:self_proc) { proc { |*args| output 'noop'; self } }
|
9
|
+
let(:add_proc) { proc { |*args| output 'added'; add 'x' } } # TODO FIXME change 'x' => valid_context somehow 'cause they are the same instance
|
10
|
+
let(:replace_proc) { proc { |*args| output 'replaced'; replace 'x' } }
|
11
|
+
let(:double_action_error_proc) { proc { |*args| output 'double action error'; drop; add 'x' } }
|
12
|
+
let(:prompt_proc) { proc { |*args| 'prompt' } }
|
13
|
+
|
14
|
+
let(:valid_context_definition) do
|
15
|
+
x = mock('Cuttlebone::Definition "x"')
|
16
|
+
x.stub!(:match).and_return() { |*args| args == [valid_context] }
|
17
|
+
x.stub!(:proc_for).with('drop').and_return(drop_proc)
|
18
|
+
x.stub!(:proc_for).with('self').and_return(self_proc)
|
19
|
+
x.stub!(:proc_for).with('add').and_return(add_proc)
|
20
|
+
x.stub!(:proc_for).with('replace').and_return(replace_proc)
|
21
|
+
x.stub!(:proc_for).with('double').and_return(double_action_error_proc)
|
22
|
+
x.stub!(:prompt).and_return(prompt_proc)
|
23
|
+
x
|
24
|
+
end
|
25
|
+
|
26
|
+
before :each do
|
27
|
+
Cuttlebone.stub!(:definitions).and_return([ valid_context_definition ])
|
28
|
+
end
|
29
|
+
|
30
|
+
context "given a valid context" do
|
31
|
+
subject { Cuttlebone::Controller.new(valid_session, valid_context) }
|
32
|
+
|
33
|
+
it "should return context object" do
|
34
|
+
subject.context.should == valid_context
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return [:self, self, nil, nil] to empty commands" do
|
38
|
+
subject.process('').should == [ :self, subject, [], nil ]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return [:drop, nil, 'dropped', nil] to 'drop' commands" do
|
42
|
+
subject.process('drop').should == [:drop, nil, ['dropped'], nil]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should return [:self, self, 'noop', nil] to 'drop' commands" do
|
46
|
+
subject.process('self').should == [:self, subject, ['noop'], nil]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return [:add, valid_context, 'added', nil] to 'drop' commands" do
|
50
|
+
subject.process('add').should == [:add, valid_context, ['added'], nil]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return [:replace, valid_context, 'replaced', nil] to 'drop' commands" do
|
54
|
+
subject.process('replace').should == [:replace, valid_context, ['replaced'], nil]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should raise error on double action commands" do
|
58
|
+
expect{ subject.process('double') }.should raise_error(Cuttlebone::DoubleActionError)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should execute command in <<context>>'s context" do
|
62
|
+
valid_context_definition.stub!(:proc_for).with('x').and_return(proc{ x })
|
63
|
+
valid_context.should_receive(:x)
|
64
|
+
subject.process('x')
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should execute prompt in <<context>>'s context" do
|
68
|
+
valid_context_definition.stub!(:prompt).and_return(proc{ x })
|
69
|
+
valid_context.should_receive(:x)
|
70
|
+
subject.prompt()
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "given an invalid context" do
|
75
|
+
it "should raise an exception" do
|
76
|
+
expect{ Cuttlebone::Controller.new(valid_session, 'invalid_context') }.should raise_error(Cuttlebone::InvalidContextError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cuttlebone do
|
4
|
+
before :each do
|
5
|
+
Cuttlebone.definitions.clear
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should start with no contexts defined" do
|
9
|
+
Cuttlebone.definitions.should be_empty
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should be able to evaluate new context definitions" do
|
13
|
+
Cuttlebone.should respond_to(:context)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should start a new session" do
|
17
|
+
Cuttlebone.should respond_to(:run)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '/spec_helper'))
|
2
|
+
|
3
|
+
describe Cuttlebone::Definition do
|
4
|
+
let(:valid_context_identifier) { :c }
|
5
|
+
let(:valid_options) { {} }
|
6
|
+
let(:valid_block) { proc { command('x') {} } }
|
7
|
+
|
8
|
+
context "given valid but meaningless parameters" do
|
9
|
+
subject { Cuttlebone::Definition.new(valid_context_identifier, valid_options, &valid_block) }
|
10
|
+
|
11
|
+
it "should match to the context id given" do
|
12
|
+
subject.should match(valid_context_identifier)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return prompt" do
|
16
|
+
subject.should respond_to(:prompt)
|
17
|
+
subject.prompt.should be_a(Proc)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "given class for context matcher" do
|
22
|
+
let(:klass) { Class.new(Object) }
|
23
|
+
let(:instance1) { klass.new }
|
24
|
+
let(:instance2) { klass.new }
|
25
|
+
let(:other_instance) { Object.new }
|
26
|
+
let(:subclass) { Class.new(klass) }
|
27
|
+
let(:instance3) { subclass.new }
|
28
|
+
subject { Cuttlebone::Definition.new(klass, valid_options, &valid_block) }
|
29
|
+
|
30
|
+
it "should match instances" do
|
31
|
+
subject.should match(instance1)
|
32
|
+
subject.should match(instance2)
|
33
|
+
subject.should_not match(other_instance)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should match subclass instances" do
|
37
|
+
subject.should match(instance3)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "given several commands" do
|
42
|
+
let(:drop_block) { proc { drop } }
|
43
|
+
let(:add_block) { proc { |arg| add arg.to_s.to_sym } }
|
44
|
+
let(:definition_with_several_commands) do
|
45
|
+
proc do
|
46
|
+
command 'nil_will_be_self' do
|
47
|
+
end
|
48
|
+
command 'drop' do
|
49
|
+
drop
|
50
|
+
end
|
51
|
+
command 'add x' do
|
52
|
+
add 0
|
53
|
+
end
|
54
|
+
command 'replace x' do
|
55
|
+
replace :x
|
56
|
+
end
|
57
|
+
command 'self' do
|
58
|
+
self
|
59
|
+
end
|
60
|
+
command 'double_action_error' do
|
61
|
+
add :x
|
62
|
+
drop
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
subject { Cuttlebone::Definition.new(valid_context_identifier, valid_options, &definition_with_several_commands) }
|
68
|
+
|
69
|
+
it "should have several commands" do
|
70
|
+
subject.should have(6).commands
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should parse new commands" do
|
74
|
+
subject.command(/new command/) { self }
|
75
|
+
subject.should have(7).commands
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return the specific block for a command" do
|
79
|
+
subject.command /^add (.)$/, &add_block
|
80
|
+
|
81
|
+
subject.proc_for('add x').should_not == [add_block, []]
|
82
|
+
subject.proc_for('add y').should == [add_block, ['y']]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should parse arguments properly" do
|
86
|
+
subject.command /^add (.)(.)?$/, &(proc{|a,b|})
|
87
|
+
|
88
|
+
subject.proc_for('add y').last.should == ['y', nil]
|
89
|
+
subject.proc_for('add yz').last.should == ['y', 'z']
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return an error for unmatched commands" do
|
93
|
+
expect{ subject.proc_for('unmatched') }.should raise_error(Cuttlebone::UnknownCommandError)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should return no error on semantically wrong commands" do
|
97
|
+
expect{ subject.proc_for('double_action_error') }.should_not raise_error
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/spec/rcov.opts
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cuttlebone::Session::Base do
|
4
|
+
let(:valid_context) { 'x' }
|
5
|
+
let(:valid_command) { 'y' }
|
6
|
+
|
7
|
+
let(:valid_context_definition) do
|
8
|
+
x = mock('Definition:x')
|
9
|
+
x.stub!(:match).and_return() { |*args| args == [valid_context] }
|
10
|
+
x.stub!(:proc_for).with(any_args()).and_return([proc{}, []])
|
11
|
+
x
|
12
|
+
end
|
13
|
+
|
14
|
+
before :each do
|
15
|
+
Cuttlebone.stub!(:definitions).and_return([ valid_context_definition ])
|
16
|
+
end
|
17
|
+
|
18
|
+
context "having an active 'x' context" do
|
19
|
+
subject { Cuttlebone::Session::Base.new(valid_context) }
|
20
|
+
|
21
|
+
it { should_not be_terminated }
|
22
|
+
|
23
|
+
it "should have no internal error" do
|
24
|
+
subject.internal_error.should be_blank
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should evaluate a string command" do
|
28
|
+
valid_context_definition.should_receive(:match).with(valid_context).and_return(true)
|
29
|
+
subject.call(valid_command)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return active context" do
|
33
|
+
subject.should respond_to(:active_context)
|
34
|
+
subject.active_context.should be_a(Cuttlebone::Controller)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return with a [action, context, output, error] quadruple" do
|
38
|
+
a, c, o, e = subject.call(valid_context)
|
39
|
+
[ :drop, :replace, :self, :add ].should include(a)
|
40
|
+
o and o.should be_a(Array)
|
41
|
+
e and e.should be_a(String)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "having no active contexts" do
|
46
|
+
subject { Cuttlebone::Session::Base.new() }
|
47
|
+
|
48
|
+
it "should have no internal error" do
|
49
|
+
subject.internal_error.should be_blank
|
50
|
+
end
|
51
|
+
|
52
|
+
it { should be_terminated }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "having 1 context" do
|
56
|
+
subject { Cuttlebone::Session::Base.new(valid_context) }
|
57
|
+
|
58
|
+
it "should have no internal error" do
|
59
|
+
subject.internal_error.should be_blank
|
60
|
+
end
|
61
|
+
|
62
|
+
it { should_not be_terminated }
|
63
|
+
|
64
|
+
it "should be terminated after a 'drop'" do
|
65
|
+
valid_context_definition.should_receive(:proc_for).with('drop').and_return([proc{drop}, []])
|
66
|
+
subject.call('drop')
|
67
|
+
subject.should be_terminated
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "given a wrong context" do
|
72
|
+
subject { Cuttlebone::Session::Base.new('invalid_context') }
|
73
|
+
|
74
|
+
it "should indicate internal error" do
|
75
|
+
subject.internal_error.should_not be_blank
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be terminated" do
|
79
|
+
subject.should be_terminated
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'cuttlebone'))
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.alias_it_should_behave_like_to(:it_should_behave_like, '')
|
6
|
+
config.filter_run :focused => true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# (active_support)/lib/active_support/core_ext/module/remove_method.rb
|
2
|
+
class Module
|
3
|
+
def remove_possible_method(method)
|
4
|
+
remove_method(method)
|
5
|
+
rescue NameError
|
6
|
+
end
|
7
|
+
|
8
|
+
def redefine_method(method, &block)
|
9
|
+
remove_possible_method(method)
|
10
|
+
define_method(method, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# (active_support)/lib/active_support/core_ext/module/delegation.rb (stripped)
|
15
|
+
class Module
|
16
|
+
def delegate(*methods)
|
17
|
+
options = methods.pop
|
18
|
+
unless options.is_a?(Hash) && to = options[:to]
|
19
|
+
raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
|
20
|
+
end
|
21
|
+
|
22
|
+
if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
|
23
|
+
raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
|
24
|
+
end
|
25
|
+
|
26
|
+
prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" || ''
|
27
|
+
|
28
|
+
file, line = caller.first.split(':', 2)
|
29
|
+
line = line.to_i
|
30
|
+
|
31
|
+
methods.each do |method|
|
32
|
+
on_nil =
|
33
|
+
if options[:allow_nil]
|
34
|
+
'return'
|
35
|
+
else
|
36
|
+
%(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
|
37
|
+
end
|
38
|
+
|
39
|
+
module_eval(<<-EOS, file, line - 5)
|
40
|
+
if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
|
41
|
+
remove_possible_method("#{prefix}#{method}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
|
45
|
+
#{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
|
46
|
+
rescue NoMethodError # rescue NoMethodError
|
47
|
+
if #{to}.nil? # if client.nil?
|
48
|
+
#{on_nil} # return # depends on :allow_nil
|
49
|
+
else # else
|
50
|
+
raise # raise
|
51
|
+
end # end
|
52
|
+
end # end
|
53
|
+
EOS
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cuttlebone
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.3
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bence Golda
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-18 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: bundler
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.0
|
24
|
+
type: :development
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 2.5.0
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: i18n
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: cucumber
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.10.0
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
61
|
+
name: rcov
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.9.0
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: *id005
|
71
|
+
description: Cuttlebone helps you creating shell-alike applications.
|
72
|
+
email:
|
73
|
+
- bence@golda.me
|
74
|
+
executables: []
|
75
|
+
|
76
|
+
extensions: []
|
77
|
+
|
78
|
+
extra_rdoc_files:
|
79
|
+
- README.md
|
80
|
+
files:
|
81
|
+
- .gitignore
|
82
|
+
- CHANGELOG.md
|
83
|
+
- Gemfile
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- cuttlebone.gemspec
|
87
|
+
- examples/todo.rb
|
88
|
+
- features/example.feature
|
89
|
+
- features/step_definitions/steps.rb
|
90
|
+
- features/support/env.rb
|
91
|
+
- features/support/test_driver.rb
|
92
|
+
- features/switching_contexts.feature
|
93
|
+
- lib/cuttlebone.rb
|
94
|
+
- lib/cuttlebone/controller.rb
|
95
|
+
- lib/cuttlebone/definition.rb
|
96
|
+
- lib/cuttlebone/exceptions.rb
|
97
|
+
- lib/cuttlebone/session.rb
|
98
|
+
- lib/cuttlebone/session/base.rb
|
99
|
+
- lib/cuttlebone/session/rack.rb
|
100
|
+
- lib/cuttlebone/session/shell.rb
|
101
|
+
- lib/cuttlebone/version.rb
|
102
|
+
- spec/controller_spec.rb
|
103
|
+
- spec/cuttlebone_spec.rb
|
104
|
+
- spec/definition_spec.rb
|
105
|
+
- spec/rcov.opts
|
106
|
+
- spec/session_base_spec.rb
|
107
|
+
- spec/spec.opts
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
- vendor/active_support.rb
|
110
|
+
has_rdoc: true
|
111
|
+
homepage: http://github.com/gbence/cuttlebone
|
112
|
+
licenses: []
|
113
|
+
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options:
|
116
|
+
- --charset=UTF-8
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 2700429317005123117
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
version: "0"
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.3.7
|
134
|
+
requirements: []
|
135
|
+
|
136
|
+
rubyforge_project: cuttlebone
|
137
|
+
rubygems_version: 1.5.3
|
138
|
+
signing_key:
|
139
|
+
specification_version: 3
|
140
|
+
summary: cuttlebone-0.1.3
|
141
|
+
test_files: []
|
142
|
+
|