shellissimo 0.0.1
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 +13 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +17 -0
- data/lib/shellissimo.rb +6 -0
- data/lib/shellissimo/command.rb +33 -0
- data/lib/shellissimo/command_name.rb +46 -0
- data/lib/shellissimo/dsl.rb +45 -0
- data/lib/shellissimo/dsl/command_builder.rb +33 -0
- data/lib/shellissimo/error_handling.rb +20 -0
- data/lib/shellissimo/input_parser.rb +44 -0
- data/lib/shellissimo/shell.rb +105 -0
- data/lib/shellissimo/version.rb +3 -0
- data/shellissimo.gemspec +24 -0
- data/test/lib/shellissimo/test_command.rb +30 -0
- data/test/lib/shellissimo/test_command_name.rb +63 -0
- data/test/lib/shellissimo/test_dsl.rb +42 -0
- data/test/lib/shellissimo/test_input_parser.rb +37 -0
- data/test/test_helper.rb +25 -0
- metadata +125 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Vladimir Yarotsky
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Shellissimo
|
2
|
+
|
3
|
+
Minimalistic framework for constructing shell-like applications.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'shellissimo'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install shellissimo
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "yard"
|
3
|
+
require "yard/rake/yardoc_task"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
desc "generate YARD documentation"
|
7
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
8
|
+
t.files = FileList['lib/**/*.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "run unit tests"
|
12
|
+
Rake::TestTask.new do |t|
|
13
|
+
t.libs << "test"
|
14
|
+
t.test_files = FileList['test/lib/**/test*.rb']
|
15
|
+
end
|
16
|
+
|
17
|
+
task :default => :test
|
data/lib/shellissimo.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shellissimo/command_name'
|
2
|
+
|
3
|
+
module Shellissimo
|
4
|
+
|
5
|
+
class Command
|
6
|
+
attr_reader :name, :aliases, :description, :params, :block
|
7
|
+
|
8
|
+
def initialize(name, description = "", aliases = [], params = [], &block)
|
9
|
+
@name, @description, @params = CommandName.new(name, aliases), String(description), params
|
10
|
+
self.block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def block=(b)
|
14
|
+
@block = b or raise ArgumentError, "command block is required"
|
15
|
+
end
|
16
|
+
|
17
|
+
def prepend_params(params)
|
18
|
+
old_block = block # let's fool ruby
|
19
|
+
dup.tap { |c| c.block = proc { instance_exec(params, &old_block) } }
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(*args)
|
23
|
+
block.call(*args)
|
24
|
+
end
|
25
|
+
alias_method :[], :call
|
26
|
+
|
27
|
+
def to_proc
|
28
|
+
block
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Shellissimo
|
2
|
+
|
3
|
+
class CommandName
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :name, :aliases
|
7
|
+
|
8
|
+
def initialize(name, aliases = [])
|
9
|
+
@name, @aliases = String(name), Array(aliases).map(&:to_s)
|
10
|
+
raise ArgumentError, "command name can't be blank" if @name.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
@name.hash ^ @aliases.hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
case other
|
19
|
+
when CommandName
|
20
|
+
return true if other.equal? self
|
21
|
+
return true if name == other.name && aliases == other.aliases
|
22
|
+
when String, Symbol
|
23
|
+
return true if other.to_s == @name
|
24
|
+
return true if @aliases.include?(other.to_s)
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def <=>(other)
|
31
|
+
self == other or name <=> other.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
result = "\e[1m#{name}\e[0m"
|
36
|
+
result += " (aliases: #{aliases.join(", ")})" unless aliases.empty?
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"<CommandName:#{self.object_id} name: #{@name.inspect}, aliases: #{@aliases.inspect}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'shellissimo/dsl/command_builder'
|
2
|
+
|
3
|
+
module Shellissimo
|
4
|
+
class CommandNotFoundError < StandardError; end
|
5
|
+
|
6
|
+
#
|
7
|
+
# Provides a DSL for defining shellissimo commands
|
8
|
+
#
|
9
|
+
module DSL
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
base.extend Macros
|
13
|
+
end
|
14
|
+
|
15
|
+
module Macros
|
16
|
+
#
|
17
|
+
# @param name [String] Primary name for command
|
18
|
+
# @yieldparam [CommandBuilder] command_builder
|
19
|
+
#
|
20
|
+
def command(name)
|
21
|
+
builder = CommandBuilder.new(name)
|
22
|
+
yield builder
|
23
|
+
commands << builder.result
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
#
|
30
|
+
# @return [Array] a list of defined commands
|
31
|
+
#
|
32
|
+
def commands
|
33
|
+
@commands ||= begin
|
34
|
+
c = Array.new
|
35
|
+
def c.find_by_name_or_alias(name_or_alias)
|
36
|
+
detect { |c| c.name == name_or_alias } or
|
37
|
+
raise CommandNotFoundError, "Command #{command_name} not found"
|
38
|
+
end
|
39
|
+
c
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shellissimo/command'
|
2
|
+
|
3
|
+
module Shellissimo
|
4
|
+
module DSL
|
5
|
+
|
6
|
+
class CommandBuilder
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def description(desc)
|
12
|
+
@description = desc
|
13
|
+
end
|
14
|
+
|
15
|
+
def shortcut(*aliases)
|
16
|
+
@aliases = Array(aliases)
|
17
|
+
end
|
18
|
+
alias :shortcuts :shortcut
|
19
|
+
|
20
|
+
#
|
21
|
+
# A block to run upon command execution
|
22
|
+
#
|
23
|
+
def run(&block)
|
24
|
+
@block = block
|
25
|
+
end
|
26
|
+
|
27
|
+
def result
|
28
|
+
Command.new(String(@name), @description, @aliases, &@block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Shellissimo
|
2
|
+
|
3
|
+
#
|
4
|
+
# Wraps code in catch-all block
|
5
|
+
#
|
6
|
+
def self.with_error_handling
|
7
|
+
begin
|
8
|
+
yield
|
9
|
+
rescue SystemExit
|
10
|
+
exit 0
|
11
|
+
rescue Exception => e
|
12
|
+
$stderr.puts "An error occured: #{e.message}"
|
13
|
+
if ENV['DEBUG']
|
14
|
+
$stderr.puts e.class.name
|
15
|
+
$stderr.puts e.backtrace
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Shellissimo
|
4
|
+
|
5
|
+
class InputParser
|
6
|
+
COMMAND_PATTERN = /(\w+)(?:\s*(.*))/
|
7
|
+
|
8
|
+
#
|
9
|
+
# @param commands [#find_by_name_or_alias] a list of commands
|
10
|
+
#
|
11
|
+
def initialize(commands)
|
12
|
+
@commands = commands
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# @param line [String] a line of input from shell
|
17
|
+
# @return [Command] a command with pre-populated params
|
18
|
+
#
|
19
|
+
def parse_command(line)
|
20
|
+
command_name, command_params = line.match(COMMAND_PATTERN)[1..2]
|
21
|
+
command = @commands.find_by_name_or_alias(command_name)
|
22
|
+
command.prepend_params(parse_params(command_params))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse_params(params_string)
|
28
|
+
params_json = normalize_json(params_string)
|
29
|
+
symbolize_hash_keys(JSON.parse(params_json))
|
30
|
+
rescue JSON::ParserError => e
|
31
|
+
raise ArgumentError, "Can not parse command params: #{e.message}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize_json(json_string)
|
35
|
+
json_string.gsub(/\A(.*)\Z/, "{\\1}").gsub(/([{,]\s*)(\w+)(\s*:\s*["\d])/, '\1"\2"\3')
|
36
|
+
end
|
37
|
+
|
38
|
+
def symbolize_hash_keys(hash)
|
39
|
+
hash.inject({}) { |h, (k, v)| h[k.to_sym] = v; h }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'readline'
|
2
|
+
require 'shellissimo/error_handling'
|
3
|
+
require 'shellissimo/dsl'
|
4
|
+
require 'shellissimo/input_parser'
|
5
|
+
|
6
|
+
module Shellissimo
|
7
|
+
|
8
|
+
#
|
9
|
+
# Base class for Shells
|
10
|
+
# Allows to define commands via DSL
|
11
|
+
# @see DSL
|
12
|
+
# Arovides 'help' and 'quit' commands out-of-box
|
13
|
+
#
|
14
|
+
# Supports REPL and one-shot mode
|
15
|
+
#
|
16
|
+
# Commands are instance-evaled against Shell instance,
|
17
|
+
# so all instance variables are available in command blocks.
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
#
|
21
|
+
# class MyShell < Shellissimo::Shell
|
22
|
+
# command :hi do |c|
|
23
|
+
# c.description "Says hello to the user"
|
24
|
+
# c.run { |params| @greeter.say_hi(params[:user]) }
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# def initialize
|
28
|
+
# @greeter = Greeter.new
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Usage:
|
33
|
+
#
|
34
|
+
# -> hi user: "vlyrs"
|
35
|
+
#
|
36
|
+
class Shell
|
37
|
+
include DSL
|
38
|
+
|
39
|
+
command :help do |c|
|
40
|
+
c.shortcut :h
|
41
|
+
c.description "Show available commands"
|
42
|
+
c.run do |*|
|
43
|
+
result = "Available commands:\n\n"
|
44
|
+
print_command = proc { |cmd| result += "%-40s - %s\n" % [cmd.name, cmd.description] }
|
45
|
+
commands.partition { |c| !%w(help quit).include? c.name.name }.each do |some_commands|
|
46
|
+
some_commands.sort_by(&:name).each(&print_command)
|
47
|
+
result += "\n"
|
48
|
+
end
|
49
|
+
result.chomp
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
command :quit do |c|
|
54
|
+
c.description "Quit #$0"
|
55
|
+
c.run { |*| exit 0 }
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(*)
|
59
|
+
@input_parser = InputParser.new(self.class.commands)
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Runs REPL
|
64
|
+
# @note swallows exceptions!
|
65
|
+
#
|
66
|
+
def run
|
67
|
+
while buf = Readline.readline(prompt, true) do
|
68
|
+
run_command(buf)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Runs a single command
|
74
|
+
# @param str [String] command and parameters (like for REPL)
|
75
|
+
# @note swallows exceptions!
|
76
|
+
#
|
77
|
+
def run_command(str)
|
78
|
+
Shellissimo.with_error_handling do
|
79
|
+
command = @input_parser.parse_command(str)
|
80
|
+
puts format instance_eval(&command.block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def prompt
|
87
|
+
'-> '
|
88
|
+
end
|
89
|
+
|
90
|
+
def format(obj)
|
91
|
+
case obj
|
92
|
+
when String
|
93
|
+
obj
|
94
|
+
when Hash
|
95
|
+
obj.map { |data| "%-20s: %s" % data }.join("\n")
|
96
|
+
when Array
|
97
|
+
obj.map(&method(:format)).join("\n")
|
98
|
+
else
|
99
|
+
obj.inspect
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
data/shellissimo.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'shellissimo/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "shellissimo"
|
8
|
+
gem.version = Shellissimo::VERSION
|
9
|
+
gem.authors = ["Vladimir Yarotsky"]
|
10
|
+
gem.email = ["vladimir.yarotsky@gmail.com"]
|
11
|
+
gem.summary = %q{Minimalistic framework for constructing shell-like applications}
|
12
|
+
gem.homepage = "https://github.com/v-yarotsky/shellissimo"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.has_rdoc = "yard"
|
20
|
+
|
21
|
+
gem.add_dependency("yard")
|
22
|
+
gem.add_dependency("redcarpet")
|
23
|
+
gem.add_development_dependency("minitest")
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'shellissimo/command'
|
3
|
+
|
4
|
+
include Shellissimo
|
5
|
+
|
6
|
+
class TestCommand < ShellissimoTestCase
|
7
|
+
test "has name as an CommandName instance" do
|
8
|
+
assert_instance_of CommandName, command("hello") { |*| }.name
|
9
|
+
end
|
10
|
+
|
11
|
+
test "can't be created without proc" do
|
12
|
+
assert_raises ArgumentError, "command proc is required" do
|
13
|
+
command("foo")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
test "is callable" do
|
18
|
+
called = false
|
19
|
+
c = command("foo") { |*| called = true }
|
20
|
+
c.call
|
21
|
+
assert called, "expected command block to be called"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def command(*args, &block)
|
27
|
+
Command.new(*args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'shellissimo/command_name'
|
3
|
+
|
4
|
+
include Shellissimo
|
5
|
+
|
6
|
+
class TestCommandName < ShellissimoTestCase
|
7
|
+
test "can not be created with blank name" do
|
8
|
+
assert_raises ArgumentError, "command name can't be blank" do
|
9
|
+
command_name(nil)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
test "CommandName instance is equal to itself" do
|
14
|
+
n = command_name("foo")
|
15
|
+
assert_equal n, n
|
16
|
+
end
|
17
|
+
|
18
|
+
test "CommandName instances are equal if both name and aliases are equal" do
|
19
|
+
assert_equal command_name("foo"), command_name("foo")
|
20
|
+
assert_equal command_name("foo", ["baz"]), command_name("foo", ["baz"])
|
21
|
+
end
|
22
|
+
|
23
|
+
test "CommandName instances are not equal if names aren't equal" do
|
24
|
+
refute_equal command_name("foo"), command_name("bar")
|
25
|
+
end
|
26
|
+
|
27
|
+
test "CommandName instances are not equal if aliases aren't equal" do
|
28
|
+
refute_equal command_name("foo", ["baz"]), command_name("foo", ["qux"])
|
29
|
+
end
|
30
|
+
|
31
|
+
test "CommandName instance is equal to a string if the string is a command's name" do
|
32
|
+
assert_equal command_name("foo"), "foo"
|
33
|
+
end
|
34
|
+
|
35
|
+
test "CommandName instance is equal to a string if the string is one of command's aliases" do
|
36
|
+
assert_equal command_name("foo", ["bar", "baz"]), "bar"
|
37
|
+
assert_equal command_name("foo", ["bar", "baz"]), "baz"
|
38
|
+
end
|
39
|
+
|
40
|
+
test "CommandName instance is not equal to a string if the string is neither command's name nor one of command's aliases" do
|
41
|
+
refute_equal command_name("foo", ["bar", "baz"]), "qux"
|
42
|
+
end
|
43
|
+
|
44
|
+
test "is sorted by name" do
|
45
|
+
assert command_name("foo") > command_name("bar"), "expected 'foo' to be greater than 'bar'"
|
46
|
+
assert command_name("bar") < command_name("baz"), "expected 'bar' to be less than 'baz'"
|
47
|
+
end
|
48
|
+
|
49
|
+
test "#to_s returns name (bright)" do
|
50
|
+
assert_equal "\e[1mfoo\e[0m", command_name("foo").to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
test "#to_s returns name (bright) and aliases if present" do
|
54
|
+
assert_equal "\e[1mfoo\e[0m (aliases: f, q)", command_name("foo", ["f", "q"]).to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def command_name(*args)
|
60
|
+
CommandName.new(*args)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'shellissimo/dsl'
|
3
|
+
|
4
|
+
include Shellissimo
|
5
|
+
|
6
|
+
class TestDsl < ShellissimoTestCase
|
7
|
+
test ".command creates a command" do
|
8
|
+
assert fake_with_command_foo.commands.find_by_name_or_alias("foo"), "expected command 'foo' to be defined"
|
9
|
+
end
|
10
|
+
|
11
|
+
test "#run inside .command sets command block" do
|
12
|
+
assert_equal :hello, fake_with_command_foo.commands.find_by_name_or_alias("foo").call
|
13
|
+
end
|
14
|
+
|
15
|
+
test "#shortcut inside .command sets command aliases" do
|
16
|
+
assert fake_with_command_foo.commands.find_by_name_or_alias("f"), "expected command 'foo' to be found by alias 'f'"
|
17
|
+
end
|
18
|
+
|
19
|
+
test "#description inside .command sets command description" do
|
20
|
+
assert_equal "foo description", fake_with_command_foo.commands.find_by_name_or_alias("foo").description
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def fake_with_command_foo
|
26
|
+
fake do
|
27
|
+
command :foo do |c|
|
28
|
+
c.shortcut :f
|
29
|
+
c.description "foo description"
|
30
|
+
c.run { :hello }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def fake(*args, &block)
|
36
|
+
Class.new do
|
37
|
+
include DSL
|
38
|
+
instance_eval(&block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'shellissimo/input_parser'
|
3
|
+
require 'shellissimo/command'
|
4
|
+
|
5
|
+
include Shellissimo
|
6
|
+
|
7
|
+
class TestInputParser < ShellissimoTestCase
|
8
|
+
test "returns command by name" do
|
9
|
+
assert_equal parser.parse_command("foo").name, "foo"
|
10
|
+
end
|
11
|
+
|
12
|
+
test "supports multiple params" do
|
13
|
+
assert_equal [{ :a => 1, :b => 2 }], parser.parse_command('foo "a": 1, "b": 2')[]
|
14
|
+
end
|
15
|
+
|
16
|
+
test "supports quoted params" do
|
17
|
+
assert_equal [{ :a => "hello, world" }], parser.parse_command('foo "a": "hello, world"')[]
|
18
|
+
end
|
19
|
+
|
20
|
+
test "supports unquoted keys" do
|
21
|
+
assert_equal [{ :a => "hello, world" }], parser.parse_command('foo a: "hello, world"')[]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def commands
|
27
|
+
cmds = [Command.new("foo") { |*args| args }]
|
28
|
+
def cmds.find_by_name_or_alias(name); first; end
|
29
|
+
cmds
|
30
|
+
end
|
31
|
+
|
32
|
+
def parser
|
33
|
+
InputParser.new(commands)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
if ENV["COVERAGE"]
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start
|
6
|
+
elsif ENV["TRAVIS"]
|
7
|
+
require 'coveralls'
|
8
|
+
Coveralls.wear!
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'minitest/unit'
|
12
|
+
require 'minitest/pride'
|
13
|
+
|
14
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
15
|
+
|
16
|
+
class ShellissimoTestCase < MiniTest::Unit::TestCase
|
17
|
+
def self.test(name, &block)
|
18
|
+
raise ArgumentError, "Example name can't be empty" if String(name).empty?
|
19
|
+
block ||= proc { skip "Not implemented yet" }
|
20
|
+
define_method "test #{name}", &block
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'minitest/autorun'
|
25
|
+
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shellissimo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vladimir Yarotsky
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '0'
|
29
|
+
prerelease: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: redcarpet
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
prerelease: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: minitest
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
prerelease: false
|
62
|
+
description:
|
63
|
+
email:
|
64
|
+
- vladimir.yarotsky@gmail.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/shellissimo.rb
|
75
|
+
- lib/shellissimo/command.rb
|
76
|
+
- lib/shellissimo/command_name.rb
|
77
|
+
- lib/shellissimo/dsl.rb
|
78
|
+
- lib/shellissimo/dsl/command_builder.rb
|
79
|
+
- lib/shellissimo/error_handling.rb
|
80
|
+
- lib/shellissimo/input_parser.rb
|
81
|
+
- lib/shellissimo/shell.rb
|
82
|
+
- lib/shellissimo/version.rb
|
83
|
+
- shellissimo.gemspec
|
84
|
+
- test/lib/shellissimo/test_command.rb
|
85
|
+
- test/lib/shellissimo/test_command_name.rb
|
86
|
+
- test/lib/shellissimo/test_dsl.rb
|
87
|
+
- test/lib/shellissimo/test_input_parser.rb
|
88
|
+
- test/test_helper.rb
|
89
|
+
homepage: https://github.com/v-yarotsky/shellissimo
|
90
|
+
licenses: []
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: 1124256564094815538
|
103
|
+
version: '0'
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
hash: 1124256564094815538
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.25
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Minimalistic framework for constructing shell-like applications
|
119
|
+
test_files:
|
120
|
+
- test/lib/shellissimo/test_command.rb
|
121
|
+
- test/lib/shellissimo/test_command_name.rb
|
122
|
+
- test/lib/shellissimo/test_dsl.rb
|
123
|
+
- test/lib/shellissimo/test_input_parser.rb
|
124
|
+
- test/test_helper.rb
|
125
|
+
has_rdoc: yard
|