shellissimo 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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