shellissimo 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b74cb3f6faba944fab76670f0d5c9769c8b44387
4
+ data.tar.gz: 920d6f8e50bb5bd2b5cc1a65fc7bf45ddf195401
5
+ SHA512:
6
+ metadata.gz: 3a40da683fcc53aa0fb493bad3ba0354cbb377bba037c127e5a9acf5fb7b897120921b5fd963bea814a69f514b6435075509361d3ae9acb6984fc8976c51440d
7
+ data.tar.gz: 2471579d9177cd250f21b9882c85ba0eb0fc24a1235b955d1654cab0937de6e14ae6ae472654f8f8e22f2d663b04b07f0fa871751acc1641e2e4b7ae196df903
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ branches:
7
+ only:
8
+ - master
data/README.md CHANGED
@@ -1,8 +1,15 @@
1
- # Shellissimo
1
+ Shellissimo
2
+ ===========
3
+
4
+ [![Build Status](https://secure.travis-ci.org/v-yarotsky/shellissimo.png)](http://travis-ci.org/v-yarotsky/shellissimo)
5
+ [![Coverage Status](https://coveralls.io/repos/v-yarotsky/shellissimo/badge.png?branch=master)](https://coveralls.io/r/v-yarotsky/shellissimo)
6
+ [![Code Climate](https://codeclimate.com/github/v-yarotsky/shellissimo.png)](https://codeclimate.com/github/v-yarotsky/shellissimo)
7
+ [![Gem Version](https://badge.fury.io/rb/shellissimo.png)](http://badge.fury.io/rb/shellissimo)
2
8
 
3
9
  Minimalistic framework for constructing shell-like applications.
4
10
 
5
- ## Installation
11
+ Installation
12
+ ---------------
6
13
 
7
14
  Add this line to your application's Gemfile:
8
15
 
@@ -16,11 +23,51 @@ Or install it yourself as:
16
23
 
17
24
  $ gem install shellissimo
18
25
 
19
- ## Usage
26
+ Usage
27
+ -----
28
+
29
+ Create your shell by sublcassing ``Shellissimo::Shell``
30
+
31
+ ```ruby
32
+ require 'shellissimo'
33
+
34
+ class Greeter; def say_hi(name, title); p "hi #{title} #{name}"; end; end
35
+
36
+ class MyShell < Shellissimo::Shell
37
+ command :hi do |c|
38
+ c.description "Says hello to the user"
39
+ c.mandatory_param(:user) do |p|
40
+ p.description "User name"
41
+ p.validate { |v| !v.to_s.strip.empty? }
42
+ end
43
+ c.param(:title) do |p|
44
+ p.validate { |v| %w(Mr. Ms.).include?(v.to_s) }
45
+ end
46
+ c.run { |params| @greeter.say_hi(params[:user], params[:title]) }
47
+ end
48
+
49
+ def initialize
50
+ @greeter = Greeter.new
51
+ super
52
+ end
53
+ end
54
+
55
+ MyShell.new.run
56
+ ```
57
+
58
+ In this example help message would look like:
59
+
60
+ Available commands:
61
+
62
+ hi - Says hello to the user
63
+ user - User name - mandatory
64
+ title - optional
20
65
 
21
- TODO: Write usage instructions here
66
+ help (aliases: h) - Show available commands
67
+ quit (aliases: exit) - Quit irb
22
68
 
23
- ## Contributing
69
+ Contributing
70
+ ------------
24
71
 
25
72
  1. Fork it
26
73
  2. Create your feature branch (`git checkout -b my-new-feature`)
@@ -1,5 +1,4 @@
1
1
  require "shellissimo/version"
2
- require "shellissimo/dsl"
3
2
  require "shellissimo/shell"
4
3
 
5
4
  module Shellissimo
@@ -3,10 +3,11 @@ require 'shellissimo/command_name'
3
3
  module Shellissimo
4
4
 
5
5
  class Command
6
- attr_reader :name, :aliases, :description, :params, :block
6
+ attr_reader :name, :aliases, :description, :param_definitions, :block
7
7
 
8
- def initialize(name, description = "", aliases = [], params = [], &block)
9
- @name, @description, @params = CommandName.new(name, aliases), String(description), params
8
+ def initialize(name, description = "", aliases = [], param_definitions = [], &block)
9
+ @name, @description = CommandName.new(name, aliases), String(description)
10
+ @param_definitions = Array(param_definitions)
10
11
  self.block = block
11
12
  end
12
13
 
@@ -15,6 +16,7 @@ module Shellissimo
15
16
  end
16
17
 
17
18
  def prepend_params(params)
19
+ params = filter_params(params)
18
20
  old_block = block # let's fool ruby
19
21
  dup.tap { |c| c.block = proc { instance_exec(params, &old_block) } }
20
22
  end
@@ -27,6 +29,17 @@ module Shellissimo
27
29
  def to_proc
28
30
  block
29
31
  end
32
+
33
+ private
34
+
35
+ def filter_params(params)
36
+ filtered = {}
37
+ @param_definitions.each do |p|
38
+ raise ArgumentError, "param '#{p.name}' is not valid!" unless p.valid?(params[p.name])
39
+ filtered[p.name] = params[p.name]
40
+ end
41
+ filtered.merge(params)
42
+ end
30
43
  end
31
44
 
32
45
  end
@@ -6,8 +6,8 @@ module Shellissimo
6
6
  attr_reader :name, :aliases
7
7
 
8
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?
9
+ raise ArgumentError, "command name can't be blank" if String(name).empty?
10
+ @name, @aliases = name.to_sym, Array(aliases).map(&:to_sym)
11
11
  end
12
12
 
13
13
  def hash
@@ -19,13 +19,16 @@ module Shellissimo
19
19
  when CommandName
20
20
  return true if other.equal? self
21
21
  return true if name == other.name && aliases == other.aliases
22
+ false
22
23
  when String, Symbol
23
- return true if other.to_s == @name
24
- return true if @aliases.include?(other.to_s)
24
+ return true if other.to_sym == @name
25
+ return true if @aliases.include?(other.to_sym)
26
+ false
25
27
  else
26
28
  false
27
29
  end
28
30
  end
31
+ alias :eql? :==
29
32
 
30
33
  def <=>(other)
31
34
  self == other or name <=> other.name
@@ -36,10 +39,6 @@ module Shellissimo
36
39
  result += " (aliases: #{aliases.join(", ")})" unless aliases.empty?
37
40
  result
38
41
  end
39
-
40
- def inspect
41
- "<CommandName:#{self.object_id} name: #{@name.inspect}, aliases: #{@aliases.inspect}"
42
- end
43
42
  end
44
43
 
45
44
  end
@@ -0,0 +1,23 @@
1
+ require 'shellissimo/command_param_validator'
2
+
3
+ module Shellissimo
4
+
5
+ class CommandParam
6
+ attr_reader :description, :validator
7
+
8
+ def initialize(name, validator = nil, description = "")
9
+ @name, @description = String(name), String(description)
10
+ raise ArgumentError, "command param name can't be blank" if @name.empty?
11
+ @validator = validator || CommandParamValidator.noop
12
+ end
13
+
14
+ def name
15
+ @name.to_sym
16
+ end
17
+
18
+ def valid?(value)
19
+ !!@validator[value]
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,42 @@
1
+ module Shellissimo
2
+
3
+ class CommandParamValidator
4
+ attr_reader :description, :block
5
+
6
+ class << self
7
+ def noop
8
+ new { |v| true }
9
+ end
10
+
11
+ def optional
12
+ new("optional") { |v| v.nil? }
13
+ end
14
+
15
+ def mandatory
16
+ new("mandatory") { |v| !v.nil? }
17
+ end
18
+ end
19
+
20
+ def initialize(description = "", &block)
21
+ @description = description
22
+ @block = block or
23
+ raise ArgumentError, "command param validator block can't be blank"
24
+ end
25
+
26
+ def call(value)
27
+ @block.call(value)
28
+ end
29
+ alias :[] :call
30
+
31
+ def &(other)
32
+ CommandParamValidator.new(description) { |value| block[value] && other.block[value] }
33
+ end
34
+
35
+ def |(other)
36
+ CommandParamValidator.new(description) { |value| block[value] || other.block[value] }
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+
@@ -0,0 +1,36 @@
1
+ require 'forwardable'
2
+
3
+ module Shellissimo
4
+
5
+ class CommandsCollection
6
+ include Enumerable
7
+ extend Forwardable
8
+
9
+ def_delegators :@commands, :size
10
+
11
+ def initialize
12
+ @commands = []
13
+ end
14
+
15
+ def find_by_name_or_alias(name_or_alias)
16
+ @commands.detect { |c| c.name == name_or_alias } or
17
+ raise CommandNotFoundError, "Command #{name_or_alias} not found"
18
+ end
19
+ alias :[] :find_by_name_or_alias
20
+
21
+ def each(&block)
22
+ @commands.each(&block)
23
+ end
24
+
25
+ def <<(command)
26
+ @commands << command
27
+ self
28
+ end
29
+
30
+ def push(*commands)
31
+ @commands.push(*commands)
32
+ self
33
+ end
34
+ end
35
+
36
+ end
@@ -1,4 +1,5 @@
1
1
  require 'shellissimo/dsl/command_builder'
2
+ require 'shellissimo/commands_collection'
2
3
 
3
4
  module Shellissimo
4
5
  class CommandNotFoundError < StandardError; end
@@ -20,23 +21,22 @@ module Shellissimo
20
21
  def command(name)
21
22
  builder = CommandBuilder.new(name)
22
23
  yield builder
23
- commands << builder.result
24
+ add_command(builder.result)
24
25
  end
25
-
26
26
  end
27
27
 
28
28
  module ClassMethods
29
29
  #
30
- # @return [Array] a list of defined commands
30
+ # @return [CommandsCollection] a list of defined commands
31
31
  #
32
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
33
+ CommandsCollection.new
34
+ end
35
+
36
+ def add_command(command)
37
+ new_commands = (commands << command)
38
+ define_singleton_method :commands do
39
+ new_commands
40
40
  end
41
41
  end
42
42
  end
@@ -1,11 +1,14 @@
1
1
  require 'shellissimo/command'
2
+ require 'shellissimo/dsl/command_param_builder'
2
3
 
3
4
  module Shellissimo
4
5
  module DSL
5
6
 
6
7
  class CommandBuilder
7
- def initialize(name)
8
+ def initialize(name, param_builder_class = CommandParamBuilder)
8
9
  @name = name
10
+ @param_definitions = []
11
+ @param_builder_class = param_builder_class
9
12
  end
10
13
 
11
14
  def description(desc)
@@ -24,8 +27,42 @@ module Shellissimo
24
27
  @block = block
25
28
  end
26
29
 
30
+ #
31
+ # Defines a single command param (optional)
32
+ # @see CommandParamBuilder
33
+ # @param name [String] name for command param
34
+ # @yieldparam [CommandParamBuilder]
35
+ # @return [CommandParam]
36
+ #
37
+ def param(name)
38
+ build_param(name) do |p|
39
+ p.optional!
40
+ yield p if block_given?
41
+ end
42
+ end
43
+
44
+ #
45
+ # Defines mandatory command param
46
+ # @see #param
47
+ #
48
+ def mandatory_param(name)
49
+ build_param(name) do |p|
50
+ p.mandatory!
51
+ yield p if block_given?
52
+ end
53
+ end
54
+
27
55
  def result
28
- Command.new(String(@name), @description, @aliases, &@block)
56
+ Command.new(String(@name), @description, @aliases, @param_definitions, &@block)
57
+ end
58
+
59
+ private
60
+
61
+ def build_param(name)
62
+ builder = @param_builder_class.new(name)
63
+ yield builder if block_given?
64
+ @param_definitions << builder.result
65
+ builder.result
29
66
  end
30
67
  end
31
68
 
@@ -0,0 +1,55 @@
1
+ require 'shellissimo/command_param'
2
+ require 'shellissimo/command_param_validator'
3
+
4
+ module Shellissimo
5
+ module DSL
6
+
7
+ class CommandParamBuilder
8
+ attr_reader :name, :description
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ @validator = CommandParamValidator.noop
13
+ @kind = :optional
14
+ end
15
+
16
+ def description(desc)
17
+ @description = desc
18
+ end
19
+
20
+ def validate(description = "", &block)
21
+ @validator = @validator & CommandParamValidator.new(description, &block)
22
+ end
23
+
24
+ #
25
+ # @private
26
+ #
27
+ def mandatory!
28
+ @kind = :mandatory
29
+ end
30
+
31
+ #
32
+ # @private
33
+ #
34
+ def optional!
35
+ @kind = :optional
36
+ end
37
+
38
+ def result
39
+ CommandParam.new(@name, validator, @description)
40
+ end
41
+
42
+ private
43
+
44
+ def validator
45
+ case @kind
46
+ when :optional
47
+ CommandParamValidator.optional | @validator
48
+ when :mandatory
49
+ CommandParamValidator.mandatory & @validator
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,52 @@
1
+ module Shellissimo
2
+
3
+ #
4
+ # Generates help secion based on commands defined for a shell
5
+ #
6
+ class Help
7
+ def initialize(commands)
8
+ @commands = commands
9
+ @rendered = ""
10
+ render
11
+ end
12
+
13
+ def rendered
14
+ @rendered.dup.chomp
15
+ end
16
+
17
+ private
18
+
19
+ def render
20
+ line "Available commands:"
21
+ line
22
+ @commands.partition { |c| !builtin_commands.include? c }.each do |cs|
23
+ cs.sort_by(&:name).each(&method(:render_command))
24
+ line
25
+ end
26
+ end
27
+
28
+ def line(str = "")
29
+ @rendered += "#{str}\n"
30
+ end
31
+
32
+ def builtin_commands
33
+ %w(help quit).map { |n| @commands[n] }
34
+ end
35
+
36
+ def render_command(cmd)
37
+ render_name_and_description(cmd.name, "%-40s", cmd.description)
38
+ cmd.param_definitions.each { |p| render_param(p) }
39
+ end
40
+
41
+ def render_param(param)
42
+ render_name_and_description(param.name, " %-30s", param.description, param.validator.description)
43
+ end
44
+
45
+ def render_name_and_description(name, name_format, *details)
46
+ formatted_name = name_format % [name]
47
+ details.map! { |d| " - %s" % [d] unless d.empty? }
48
+ line String(formatted_name) + details.join
49
+ end
50
+ end
51
+
52
+ end
@@ -6,7 +6,7 @@ module Shellissimo
6
6
  COMMAND_PATTERN = /(\w+)(?:\s*(.*))/
7
7
 
8
8
  #
9
- # @param commands [#find_by_name_or_alias] a list of commands
9
+ # @param commands [CommandsCollection] a list of commands
10
10
  #
11
11
  def initialize(commands)
12
12
  @commands = commands
@@ -18,7 +18,7 @@ module Shellissimo
18
18
  #
19
19
  def parse_command(line)
20
20
  command_name, command_params = line.match(COMMAND_PATTERN)[1..2]
21
- command = @commands.find_by_name_or_alias(command_name)
21
+ command = @commands[command_name]
22
22
  command.prepend_params(parse_params(command_params))
23
23
  end
24
24
 
@@ -2,6 +2,7 @@ require 'readline'
2
2
  require 'shellissimo/error_handling'
3
3
  require 'shellissimo/dsl'
4
4
  require 'shellissimo/input_parser'
5
+ require 'shellissimo/help'
5
6
 
6
7
  module Shellissimo
7
8
 
@@ -39,18 +40,11 @@ module Shellissimo
39
40
  command :help do |c|
40
41
  c.shortcut :h
41
42
  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
43
+ c.run { |*| Help.new(self.class.commands).rendered }
51
44
  end
52
45
 
53
46
  command :quit do |c|
47
+ c.shortcut :exit
54
48
  c.description "Quit #$0"
55
49
  c.run { |*| exit 0 }
56
50
  end
@@ -1,3 +1,3 @@
1
1
  module Shellissimo
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -18,7 +18,10 @@ Gem::Specification.new do |gem|
18
18
 
19
19
  gem.has_rdoc = "yard"
20
20
 
21
- gem.add_dependency("yard")
22
- gem.add_dependency("redcarpet")
21
+ gem.add_dependency("json")
22
+
23
+ gem.add_development_dependency("yard")
24
+ gem.add_development_dependency("redcarpet")
23
25
  gem.add_development_dependency("minitest")
26
+ gem.add_development_dependency("minitest-reporters")
24
27
  end
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+ require 'shellissimo/dsl/command_builder'
3
+
4
+ include Shellissimo::DSL
5
+
6
+ class TestCommandBuilder < ShellissimoTestCase
7
+ class FakeParamBuilder
8
+ attr_reader :mandatory, :optional
9
+ def initialize(*); @mandatory = false; end
10
+ def mandatory!; @mandatory = true; end
11
+ def optional!; @optional = true; end
12
+ def result; end
13
+ end
14
+
15
+ test "sets command name" do
16
+ assert_equal builder("foo").result.name, "foo"
17
+ end
18
+
19
+ test "#description sets command description" do
20
+ assert_equal "bar", builder("foo") { |c| c.description "bar" }.result.description
21
+ end
22
+
23
+ test "#shortcut sets command aliases" do
24
+ assert_equal builder("foo") { |c| c.shortcut :f }.result.name, "f"
25
+ end
26
+
27
+ test "#run sets command block" do
28
+ assert_equal :hi, builder("foo") { |c| c.run { :hi } }.result.call
29
+ end
30
+
31
+ test "#param creates a param" do
32
+ assert_includes builder("foo") { |c| c.param("bar") }.result.param_definitions.map(&:name), :bar
33
+ end
34
+
35
+ test "#param yields CommandParamBuilder" do
36
+ param_builder = nil
37
+ builder("foo") { |c| c.param("bar") { |p| param_builder = p } }
38
+ assert_instance_of CommandParamBuilder, param_builder
39
+ end
40
+
41
+ test "#param returns CommandParam" do
42
+ param = nil
43
+ builder("foo") { |c| param = c.param("bar") }
44
+ assert_instance_of Shellissimo::CommandParam, param
45
+ end
46
+
47
+ test "#param yields builder for optional param" do
48
+ param_builder = nil
49
+ builder("foo", FakeParamBuilder) { |c| c.param("bar") { |p| param_builder = p } }
50
+ assert param_builder.optional, "expected param to be built as optional"
51
+ end
52
+
53
+ test "#mandatory_param yields builder with presence validator" do
54
+ param_builder = nil
55
+ builder("foo", FakeParamBuilder) { |c| c.mandatory_param("bar") { |p| param_builder = p } }
56
+ assert param_builder.mandatory, "expected param to be built as mandatory"
57
+ end
58
+
59
+ private
60
+
61
+ def builder(name, *args)
62
+ c = CommandBuilder.new(name, *args)
63
+ c.run {}
64
+ yield c if block_given?
65
+ c
66
+ end
67
+ end
68
+
69
+
@@ -0,0 +1,41 @@
1
+ require 'test_helper'
2
+ require 'shellissimo/dsl/command_param_builder'
3
+
4
+ include Shellissimo::DSL
5
+
6
+ class TestCommandParamBuilder < ShellissimoTestCase
7
+ test "sets command param name" do
8
+ assert_equal builder("foo").result.name, :foo
9
+ end
10
+
11
+ test "#description sets command param description" do
12
+ assert_equal "bar", builder("foo") { |c| c.description "bar" }.result.description
13
+ end
14
+
15
+ test "#mandatory! sets stock validator to mandatory" do
16
+ refute builder("foo") { |c| c.mandatory! }.result.valid?(nil), "expected param to be mandatory"
17
+ end
18
+
19
+ test "#validate sets validator" do
20
+ called = false
21
+ param = builder("foo") { |c| c.validate { |v| called = true } }.result
22
+ param.valid?(:anything)
23
+ assert called, "expected validation block to be set"
24
+ end
25
+
26
+ test "#validate respects mandatory/optional" do
27
+ param = builder("foo") { |c| c.optional!; c.validate { |v| v == 1 } }.result
28
+ assert param.valid?(nil), "expected param to be optional"
29
+ refute param.valid?(2), "expected param to be checked by custom validator"
30
+ end
31
+
32
+ private
33
+
34
+ def builder(name)
35
+ c = CommandParamBuilder.new(name)
36
+ yield c if block_given?
37
+ c
38
+ end
39
+ end
40
+
41
+
@@ -1,5 +1,6 @@
1
1
  require 'test_helper'
2
2
  require 'shellissimo/command'
3
+ require 'ostruct'
3
4
 
4
5
  include Shellissimo
5
6
 
@@ -21,10 +22,33 @@ class TestCommand < ShellissimoTestCase
21
22
  assert called, "expected command block to be called"
22
23
  end
23
24
 
25
+ test "#prepend_params return command copy with curried block" do
26
+ received_params = {}
27
+ c = command("foo") { |params| received_params = params }
28
+ c = c.prepend_params(:foo => 1, :bar => 2)
29
+ c.call
30
+ assert_equal ({ :foo => 1, :bar => 2 }), received_params
31
+ end
32
+
33
+ test "#prepend_params runs validations over params" do
34
+ param_definition = sample_param_definition
35
+ def param_definition.valid?(*); false; end
36
+ c = command("foo", "", [], [param_definition]) { |params| received_params = params }
37
+ assert_raises ArgumentError do
38
+ c.prepend_params(:removed => 1, :bar => 2)
39
+ end
40
+ end
41
+
24
42
  private
25
43
 
26
44
  def command(*args, &block)
27
45
  Command.new(*args, &block)
28
46
  end
47
+
48
+ def sample_param_definition
49
+ param_definition = OpenStruct.new(:name => :bar)
50
+ def param_definition.valid?(*); true; end
51
+ param_definition
52
+ end
29
53
  end
30
54
 
@@ -10,37 +10,45 @@ class TestCommandName < ShellissimoTestCase
10
10
  end
11
11
  end
12
12
 
13
- test "CommandName instance is equal to itself" do
13
+ test "is equal to itself" do
14
14
  n = command_name("foo")
15
15
  assert_equal n, n
16
16
  end
17
17
 
18
- test "CommandName instances are equal if both name and aliases are equal" do
18
+ test "instances are equal if name and aliases are equal" do
19
19
  assert_equal command_name("foo"), command_name("foo")
20
20
  assert_equal command_name("foo", ["baz"]), command_name("foo", ["baz"])
21
21
  end
22
22
 
23
- test "CommandName instances are not equal if names aren't equal" do
23
+ test "instances are not equal if names aren't equal" do
24
24
  refute_equal command_name("foo"), command_name("bar")
25
25
  end
26
26
 
27
- test "CommandName instances are not equal if aliases aren't equal" do
27
+ test "instances are not equal if aliases aren't equal" do
28
28
  refute_equal command_name("foo", ["baz"]), command_name("foo", ["qux"])
29
29
  end
30
30
 
31
- test "CommandName instance is equal to a string if the string is a command's name" do
31
+ test "is equal to a string if the string is a command's name" do
32
32
  assert_equal command_name("foo"), "foo"
33
33
  end
34
34
 
35
- test "CommandName instance is equal to a string if the string is one of command's aliases" do
35
+ test "is equal to a string if it is one of command's aliases" do
36
36
  assert_equal command_name("foo", ["bar", "baz"]), "bar"
37
37
  assert_equal command_name("foo", ["bar", "baz"]), "baz"
38
38
  end
39
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
40
+ test "is not equal to a string that is neither name nor an alias" do
41
41
  refute_equal command_name("foo", ["bar", "baz"]), "qux"
42
42
  end
43
43
 
44
+ test "is not equal to crap" do
45
+ refute_equal command_name("foo"), 1
46
+ end
47
+
48
+ test "hashes of equal instances are equal" do
49
+ assert_equal command_name("foo").hash, command_name("foo").hash
50
+ end
51
+
44
52
  test "is sorted by name" do
45
53
  assert command_name("foo") > command_name("bar"), "expected 'foo' to be greater than 'bar'"
46
54
  assert command_name("bar") < command_name("baz"), "expected 'bar' to be less than 'baz'"
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+ require 'shellissimo/command_param'
3
+
4
+ include Shellissimo
5
+
6
+ class TestCommandParam < ShellissimoTestCase
7
+ test "can't be created without name" do
8
+ assert_raises ArgumentError, "command param name can't be blank" do
9
+ param(nil)
10
+ end
11
+ end
12
+
13
+ test "can have a description" do
14
+ assert_equal "foo desc", param("foo", :noop, "foo desc").description
15
+ end
16
+
17
+ test "is always valid by default" do
18
+ assert param("foo").valid?(:literally_anything), "expected param to be valid by default"
19
+ end
20
+
21
+ test "param name is a symbol" do
22
+ assert_equal :foo, param("foo").name
23
+ end
24
+
25
+ private
26
+
27
+ def param(*args)
28
+ CommandParam.new(*args)
29
+ end
30
+ end
31
+
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+ require 'shellissimo/command_param_validator'
3
+
4
+ include Shellissimo
5
+
6
+ class TestCommandParamValidator < ShellissimoTestCase
7
+ test "can have a description" do
8
+ assert_equal "foo desc", (validator("foo desc") {}).description
9
+ end
10
+
11
+ test "noop is always valid" do
12
+ assert noop_validator[:literally_anything], "expected param to be valid by default"
13
+ end
14
+
15
+ test "mandatory is not valid if nil" do
16
+ refute mandatory_validator[nil], "expected mandatory param not to be valid by if nil"
17
+ end
18
+
19
+ test "mandatory is valid if non-nil" do
20
+ assert mandatory_validator[1], "expected mandatory param to be valid by if non-nil"
21
+ end
22
+
23
+ test "allows combining validator with logical &" do
24
+ v = mandatory_validator & validator("custom") { |v| v.nil? || v == 1 }
25
+ refute v[nil], "expected mandatory validator to work"
26
+ refute v[2], "expected custom validator to work"
27
+ assert v[1], "expected both validators to work"
28
+ end
29
+
30
+ test "allows combining validator with logical |" do
31
+ v = validator("v1") { |v| v == 1 } | validator("v2") { |v| v == 2 }
32
+ assert v[1], "expected first custom validator to work"
33
+ assert v[2], "expected second custom validator to work"
34
+ refute v[0], "expected not to do whats not expected :)"
35
+ end
36
+
37
+ private
38
+
39
+ def validator(description, &block)
40
+ CommandParamValidator.new(description, &block)
41
+ end
42
+
43
+ [:noop, :optioal, :mandatory].each do |kind|
44
+ define_method "#{kind}_validator" do
45
+ CommandParamValidator.public_send(kind)
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+ require 'shellissimo/commands_collection'
3
+ require 'ostruct'
4
+
5
+ include Shellissimo
6
+
7
+ class TestCommandsCollection < ShellissimoTestCase
8
+ test "#find_by_name_or_alias returns command by name" do
9
+ assert_equal command("foo"), collection(command("foo")).find_by_name_or_alias("foo")
10
+ end
11
+
12
+ test "#[] returns command by name" do
13
+ assert_equal command("foo"), collection(command("foo"))["foo"]
14
+ end
15
+
16
+ test "is enumerable" do
17
+ c = collection(command("foo"), command("bar"))
18
+ assert_kind_of Enumerable, c
19
+ assert_equal [command("bar")], c.select { |c| c.name == "bar" }
20
+ end
21
+
22
+ private
23
+
24
+ def collection(*commands)
25
+ result = CommandsCollection.new
26
+ result.push *commands
27
+ end
28
+
29
+ def command(name)
30
+ OpenStruct.new(:name => name)
31
+ end
32
+ end
33
+
@@ -5,34 +5,25 @@ include Shellissimo
5
5
 
6
6
  class TestDsl < ShellissimoTestCase
7
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
8
+ f = dsl do
27
9
  command :foo do |c|
28
- c.shortcut :f
29
- c.description "foo description"
30
10
  c.run { :hello }
31
11
  end
32
12
  end
13
+ assert f.commands["foo"], "expected command 'foo' to be defined"
33
14
  end
34
15
 
35
- def fake(*args, &block)
16
+ test ".command yields a builder" do
17
+ builder = nil
18
+ dsl do
19
+ command(:foo) { |c| c.run {}; builder = c }
20
+ end
21
+ assert_instance_of DSL::CommandBuilder, builder
22
+ end
23
+
24
+ private
25
+
26
+ def dsl(*args, &block)
36
27
  Class.new do
37
28
  include DSL
38
29
  instance_eval(&block)
@@ -1,6 +1,7 @@
1
1
  require 'test_helper'
2
2
  require 'shellissimo/input_parser'
3
3
  require 'shellissimo/command'
4
+ require 'shellissimo/command_param'
4
5
 
5
6
  include Shellissimo
6
7
 
@@ -24,9 +25,7 @@ class TestInputParser < ShellissimoTestCase
24
25
  private
25
26
 
26
27
  def commands
27
- cmds = [Command.new("foo") { |*args| args }]
28
- def cmds.find_by_name_or_alias(name); first; end
29
- cmds
28
+ { "foo" => Command.new("foo") { |*args| args } }
30
29
  end
31
30
 
32
31
  def parser
@@ -1,17 +1,23 @@
1
1
  require 'rubygems'
2
+ require 'bundler/setup'
2
3
 
3
- if ENV["COVERAGE"]
4
+ lib = File.expand_path('../../lib', __FILE__)
5
+ $:.unshift lib
6
+
7
+ if ENV["TRAVIS"]
4
8
  require 'simplecov'
5
- SimpleCov.start
6
- elsif ENV["TRAVIS"]
7
9
  require 'coveralls'
8
- Coveralls.wear!
10
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
11
+ SimpleCov.start do
12
+ add_filter "/test/"
13
+ end
14
+ Dir.glob(File.join(lib, "**", "*.rb")).each { |f| require f }
9
15
  end
10
16
 
11
17
  require 'minitest/unit'
12
- require 'minitest/pride'
18
+ require 'minitest/reporters'
13
19
 
14
- $:.unshift File.expand_path('../../lib', __FILE__)
20
+ MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new
15
21
 
16
22
  class ShellissimoTestCase < MiniTest::Unit::TestCase
17
23
  def self.test(name, &block)
metadata CHANGED
@@ -1,64 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shellissimo
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.0.1
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Vladimir Yarotsky
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-05-08 00:00:00.000000000 Z
11
+ date: 2013-05-09 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: yard
14
+ name: json
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
21
+ prerelease: false
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
- none: false
25
23
  requirements:
26
- - - ! '>='
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
27
32
  - !ruby/object:Gem::Version
28
33
  version: '0'
34
+ type: :development
29
35
  prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: redcarpet
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ! '>='
45
+ - - '>='
36
46
  - !ruby/object:Gem::Version
37
47
  version: '0'
38
- type: :runtime
48
+ type: :development
49
+ prerelease: false
39
50
  version_requirements: !ruby/object:Gem::Requirement
40
- none: false
41
51
  requirements:
42
- - - ! '>='
52
+ - - '>='
43
53
  - !ruby/object:Gem::Version
44
54
  version: '0'
45
- prerelease: false
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: minitest
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ! '>='
59
+ - - '>='
52
60
  - !ruby/object:Gem::Version
53
61
  version: '0'
54
62
  type: :development
63
+ prerelease: false
55
64
  version_requirements: !ruby/object:Gem::Requirement
56
- none: false
57
65
  requirements:
58
- - - ! '>='
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest-reporters
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
59
74
  - !ruby/object:Gem::Version
60
75
  version: '0'
76
+ type: :development
61
77
  prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
62
83
  description:
63
84
  email:
64
85
  - vladimir.yarotsky@gmail.com
@@ -67,6 +88,7 @@ extensions: []
67
88
  extra_rdoc_files: []
68
89
  files:
69
90
  - .gitignore
91
+ - .travis.yml
70
92
  - Gemfile
71
93
  - LICENSE.txt
72
94
  - README.md
@@ -74,51 +96,59 @@ files:
74
96
  - lib/shellissimo.rb
75
97
  - lib/shellissimo/command.rb
76
98
  - lib/shellissimo/command_name.rb
99
+ - lib/shellissimo/command_param.rb
100
+ - lib/shellissimo/command_param_validator.rb
101
+ - lib/shellissimo/commands_collection.rb
77
102
  - lib/shellissimo/dsl.rb
78
103
  - lib/shellissimo/dsl/command_builder.rb
104
+ - lib/shellissimo/dsl/command_param_builder.rb
79
105
  - lib/shellissimo/error_handling.rb
106
+ - lib/shellissimo/help.rb
80
107
  - lib/shellissimo/input_parser.rb
81
108
  - lib/shellissimo/shell.rb
82
109
  - lib/shellissimo/version.rb
83
110
  - shellissimo.gemspec
111
+ - test/lib/shellissimo/dsl/test_command_builder.rb
112
+ - test/lib/shellissimo/dsl/test_command_param_builder.rb
84
113
  - test/lib/shellissimo/test_command.rb
85
114
  - test/lib/shellissimo/test_command_name.rb
115
+ - test/lib/shellissimo/test_command_param.rb
116
+ - test/lib/shellissimo/test_command_param_validator.rb
117
+ - test/lib/shellissimo/test_commands_collection.rb
86
118
  - test/lib/shellissimo/test_dsl.rb
87
119
  - test/lib/shellissimo/test_input_parser.rb
88
120
  - test/test_helper.rb
89
121
  homepage: https://github.com/v-yarotsky/shellissimo
90
122
  licenses: []
123
+ metadata: {}
91
124
  post_install_message:
92
125
  rdoc_options: []
93
126
  require_paths:
94
127
  - lib
95
128
  required_ruby_version: !ruby/object:Gem::Requirement
96
- none: false
97
129
  requirements:
98
- - - ! '>='
130
+ - - '>='
99
131
  - !ruby/object:Gem::Version
100
- segments:
101
- - 0
102
- hash: 1124256564094815538
103
132
  version: '0'
104
133
  required_rubygems_version: !ruby/object:Gem::Requirement
105
- none: false
106
134
  requirements:
107
- - - ! '>='
135
+ - - '>='
108
136
  - !ruby/object:Gem::Version
109
- segments:
110
- - 0
111
- hash: 1124256564094815538
112
137
  version: '0'
113
138
  requirements: []
114
139
  rubyforge_project:
115
- rubygems_version: 1.8.25
140
+ rubygems_version: 2.0.3
116
141
  signing_key:
117
- specification_version: 3
142
+ specification_version: 4
118
143
  summary: Minimalistic framework for constructing shell-like applications
119
144
  test_files:
145
+ - test/lib/shellissimo/dsl/test_command_builder.rb
146
+ - test/lib/shellissimo/dsl/test_command_param_builder.rb
120
147
  - test/lib/shellissimo/test_command.rb
121
148
  - test/lib/shellissimo/test_command_name.rb
149
+ - test/lib/shellissimo/test_command_param.rb
150
+ - test/lib/shellissimo/test_command_param_validator.rb
151
+ - test/lib/shellissimo/test_commands_collection.rb
122
152
  - test/lib/shellissimo/test_dsl.rb
123
153
  - test/lib/shellissimo/test_input_parser.rb
124
154
  - test/test_helper.rb