rubikon 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,26 +1,79 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
2
- # terms of the new BSD License.
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2009, Sebastian Staudt
4
+ # Copyright (c) 2009-2010, Sebastian Staudt
5
5
 
6
6
  module Rubikon
7
7
 
8
- class ArgumentTypeError < ArgumentError
8
+ # Raised by commands if no block is given and no corresponding command file
9
+ # exists
10
+ #
11
+ # @author Sebastian Staudt
12
+ # @see Command
13
+ # @since 0.1.0
14
+ class BlockMissingError < ArgumentError
9
15
  end
10
16
 
11
- class BlockMissingError < ArgumentError
17
+ # Raised by parameters that have been supplied with more arguments than they
18
+ # take
19
+ #
20
+ # @author Sebastian Staudt
21
+ # @see Parameter
22
+ # @see 0.3.0
23
+ class ExtraArgumentError < ArgumentError
24
+
25
+ def initialize(parameter)
26
+ super "Parameter #{parameter} has one or more extra arguments."
27
+ end
28
+
12
29
  end
13
30
 
31
+ # Raised by parameters that have been supplied with not all required
32
+ # arguments
33
+ #
34
+ # @author Sebastian Staudt
35
+ # @see Parameter
36
+ # @since 0.1.0
14
37
  class MissingArgumentError < ArgumentError
38
+
39
+ def initialize(parameter)
40
+ super "Parameter #{parameter} is missing one or more arguments."
41
+ end
42
+
15
43
  end
16
44
 
17
- class MissingOptionError < ArgumentError
45
+ # Raised if the user did not specify a command and no default command exists
46
+ #
47
+ # @author Sebastian Staudt
48
+ # @since 0.3.0
49
+ class NoDefaultCommandError < ArgumentError
50
+
51
+ def initialize
52
+ super 'You did not specify a command and there is no default command.'
53
+ end
54
+
55
+ end
56
+
57
+ # Raised if a command has been supplied that does not exist
58
+ #
59
+ # @author Sebastian Staudt
60
+ # @since 0.3.0
61
+ class UnknownCommandError < ArgumentError
62
+
63
+ def initialize(name)
64
+ super "Unknown command: #{name}"
65
+ end
66
+
18
67
  end
19
68
 
20
- class UnknownOptionError < ArgumentError
69
+ # Raised if a parameter has been supplied that does not exist
70
+ #
71
+ # @author Sebastian Staudt
72
+ # @since 0.3.0
73
+ class UnknownParameterError < ArgumentError
21
74
 
22
- def initialize(arg)
23
- super "Unknown argument: #{arg}"
75
+ def initialize(name)
76
+ super "Unknown parameter: #{name}"
24
77
  end
25
78
 
26
79
  end
@@ -0,0 +1,56 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2010, Sebastian Staudt
5
+
6
+ require 'rubikon/parameter'
7
+
8
+ module Rubikon
9
+
10
+ # A flag is an application parameter without arguments
11
+ #
12
+ # @author Sebastian Staudt
13
+ # @see Application::InstanceMethods#flag
14
+ # @see Application::InstanceMethods#global_flag
15
+ # @see Parameter
16
+ # @since 0.3.0
17
+ class Flag
18
+
19
+ include Parameter
20
+
21
+ # Creates a new flag with the given name and an optional code block
22
+ #
23
+ # @param name (see Parameter#initialize)
24
+ # @param block (see Parameter#initialize)
25
+ def initialize(name, &block)
26
+ super(name, 0, &block)
27
+ end
28
+
29
+ # Adds an argument to this flag
30
+ #
31
+ # @param arg (see Parameter#<<)
32
+ # @raise [ExtraArgumentError] is raised because flags never take any
33
+ # arguments.
34
+ def <<(arg)
35
+ raise ExtraArgumentError.new(@name)
36
+ end
37
+
38
+ # Checks whether this flag has all required arguments supplied
39
+ #
40
+ # @return [true] This is always +true+ because flags never take any
41
+ # arguments.
42
+ def args_full?
43
+ true
44
+ end
45
+
46
+ # Checks whether this flag can take more arguments
47
+ #
48
+ # @return [false] This is always +false+ because flags never take any
49
+ # arguments.
50
+ def more_args?
51
+ false
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,30 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2010, Sebastian Staudt
5
+
6
+ require 'rubikon/parameter'
7
+
8
+ module Rubikon
9
+
10
+ # An option is an application parameter that may have one or more additional
11
+ # arguments.
12
+ #
13
+ # @author Sebastian Staudt
14
+ # @see Application::InstanceMethods#option
15
+ # @see Parameter
16
+ # @since 0.3.0
17
+ class Option
18
+
19
+ # @return [Numeric] The number of arguments this parameter takes
20
+ attr_reader :arg_count
21
+
22
+ # @return [Array<String>] The arguments given to this parameter
23
+ attr_reader :args
24
+ alias_method :arguments, :args
25
+
26
+ include Parameter
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,101 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2010, Sebastian Staudt
5
+
6
+ module Rubikon
7
+
8
+ # A parameter is any command-line argument given to the application that is
9
+ # not prefixed with one or two dashes. Once a parameter is supplied by the
10
+ # user, it is relayed to the Command it belongs to.
11
+ #
12
+ # @author Sebastian Staudt
13
+ # @since 0.3.0
14
+ module Parameter
15
+
16
+ # @return [Array<Symbol>] The alias names of this parameter
17
+ attr_reader :aliases
18
+
19
+ # @return [Symbol] The primary name of this parameter
20
+ attr_reader :name
21
+
22
+ # Creates a new parameter with the given name
23
+ #
24
+ # @param [Symbol, #to_sym] name The name of the parameter
25
+ # @param [Numeric] arg_count The number of arguments this parameter takes
26
+ # if any
27
+ # @param [Proc] block An optional code block to be executed if this
28
+ # parameter is used
29
+ #
30
+ # A positive argument count indicates the exact amount of required
31
+ # arguments, while a negative argument count indicates the amount of
32
+ # required arguments, but allows additional, optional arguments. A argument
33
+ # count of 0 means there are no required arguments, but it allows optional
34
+ # arguments. If you need a parameter that does not allow arguments at all
35
+ # you should use a flag instead.
36
+ def initialize(name, arg_count = 0, &block)
37
+ @active = false
38
+ @aliases = []
39
+ @arg_count = arg_count
40
+ @args = []
41
+ @block = block
42
+ @name = name.to_sym
43
+ end
44
+
45
+ # Adds an argument to this parameter. Parameter arguments can be accessed
46
+ # inside the Application code using the parameter's args method.
47
+ #
48
+ # @param [String] arg The argument to add to the supplied arguments of this
49
+ # parameter
50
+ # @raise [ExtraArgumentError] if the parameter has all required arguments
51
+ # supplied and does not take optional arguments
52
+ # @return [Array] The supplied arguments of this parameter
53
+ def <<(arg)
54
+ raise ExtraArgumentError.new(@name) if args_full? && @arg_count > 0
55
+ @args << arg
56
+ end
57
+
58
+ # Marks this parameter as active when it has been supplied by the user on
59
+ # the command-line. This also calls the code block of the parameter if it
60
+ # exists
61
+ def active!
62
+ @active = true
63
+ @block.call unless @block.nil?
64
+ end
65
+
66
+ # Returns whether this parameter has is active, i.e. it has been supplied
67
+ # by the user on the command-line
68
+ #
69
+ # @return +true+ if this parameter has been supplied on the command-line
70
+ def active?
71
+ @active
72
+ end
73
+
74
+ # Checks whether this parameter has all required arguments supplied
75
+ #
76
+ # @return +true+ if all required parameter arguments have been supplied
77
+ def args_full?
78
+ arg_count = @arg_count
79
+ arg_count = -arg_count if arg_count < 0
80
+
81
+ arg_count == 0 || @args.size >= arg_count
82
+ end
83
+
84
+ # Checks the arguments for this parameter
85
+ #
86
+ # @raise [MissingArgumentError] if there are not enough arguments for
87
+ # this parameter
88
+ def check_args
89
+ raise MissingArgumentError.new(@name) unless args_full?
90
+ end
91
+
92
+ # Checks whether this parameter can take more arguments
93
+ #
94
+ # @return +true+ if this parameter can take more arguments
95
+ def more_args?
96
+ arg_count <= 0 || @args.size < arg_count
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -1,24 +1,31 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
2
- # terms of the new BSD License.
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2009, Sebastian Staudt
4
+ # Copyright (c) 2009-2010, Sebastian Staudt
5
5
 
6
6
  module Rubikon
7
7
 
8
8
  # A class for displaying and managing progress bars
9
+ #
10
+ # @author Sebastian Staudt
11
+ # @see Application::InstanceMethods#throbber
12
+ # @since 0.2.0
9
13
  class ProgressBar
10
14
 
11
15
  # Create a new ProgressBar using the given options.
12
16
  #
13
- # +ostream+:: The output stream where the progress bar should be displayed
14
- # +options+:: An Hash of options to customize the progress bar
17
+ # @param [Hash, Numeric] options A Hash of options to customize the
18
+ # progress bar or the maximum value of the progress bar
19
+ # @see Application::InstanceMethods#progress_bar
15
20
  #
16
- # Options:
17
- #
18
- # +char+:: The character used for progress bar display (default: +#+)
19
- # +maximum+:: The maximum value of this progress bar (default: +100+)
20
- # +size+:: The actual size of the progress bar (default: +20+)
21
- # +start+:: The start value of the progress bar (default: +0+)
21
+ # @option options [String] :char ('#') The character used for progress bar
22
+ # display
23
+ # @option options [Numeric] :maximum (100) The maximum value of this
24
+ # progress bar
25
+ # @option options [IO] :ostream ($stdout) The output stream where the
26
+ # progress bar should be displayed
27
+ # @option options [Numeric] :size (20) The actual size of the progress bar
28
+ # @option options [Numeric] :start (0) The start value of the progress bar
22
29
  def initialize(options = {})
23
30
  if options.is_a? Numeric
24
31
  @maximum = options
@@ -43,8 +50,10 @@ module Rubikon
43
50
  # This triggers a refresh of the progress bar, if the added value actually
44
51
  # changes the displayed bar.
45
52
  #
46
- # Example:
53
+ # @param [Numeric] value The amount to add to the progress bar
54
+ # @return [ProgressBar] The progress bar itself
47
55
  #
56
+ # @example Different alternatives to increase the progress
48
57
  # progress_bar + 1 # (will add 1)
49
58
  # progress_bar + 5 # (will add 5)
50
59
  # progress_bar.+ # (will add 1)
@@ -65,6 +74,8 @@ module Rubikon
65
74
  @ostream.flush
66
75
  @ostream.puts '' if @progress == @size
67
76
  end
77
+
78
+ self
68
79
  end
69
80
 
70
81
  end
@@ -1,11 +1,15 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
2
- # terms of the new BSD License.
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2009, Sebastian Staudt
4
+ # Copyright (c) 2009-2010, Sebastian Staudt
5
5
 
6
6
  module Rubikon
7
7
 
8
8
  # A class for displaying and managing throbbers
9
+ #
10
+ # @author Sebastian Staudt
11
+ # @see Application::InstanceMethods#throbber
12
+ # @since 0.2.0
9
13
  class Throbber < Thread
10
14
 
11
15
  SPINNER = '-\|/'
@@ -13,8 +17,9 @@ module Rubikon
13
17
  # Creates and runs a Throbber that outputs to the given IO stream while the
14
18
  # given thread is alive
15
19
  #
16
- # # +ostream+:: The IO stream the throbber should be written to
17
- # +thread+:: The thread that should be watched
20
+ # @param [IO] ostream the IO stream the throbber should be written to
21
+ # @param [Thread] thread The thread that should be watched
22
+ # @see Application::InstanceMethods#throbber
18
23
  def initialize(ostream, thread)
19
24
  proc = Proc.new do |ostream, thread|
20
25
  step = 0
data/lib/rubikon.rb CHANGED
@@ -1,9 +1,20 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
2
- # terms of the new BSD License.
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2009, Sebastian Staudt
4
+ # Copyright (c) 2009-2010, Sebastian Staudt
5
5
 
6
6
  libdir = File.dirname(__FILE__)
7
7
  $:.unshift(libdir) unless $:.include?(libdir)
8
8
 
9
+ require 'core_ext/string'
9
10
  require 'rubikon/application/base'
11
+
12
+ # A namespace module for all Rubikon related code
13
+ #
14
+ # @author Sebastian Staudt
15
+ # @since 0.1.0
16
+ module Rubikon
17
+
18
+ VERSION = '0.3.0'
19
+
20
+ end
@@ -1,102 +1,66 @@
1
- # This code is free software; you can redistribute it and/or modify it under the
2
- # terms of the new BSD License.
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
3
  #
4
- # Copyright (c) 2009, Sebastian Staudt
4
+ # Copyright (c) 2009-2010, Sebastian Staudt
5
5
 
6
6
  require 'test_helper'
7
- require 'testapp'
7
+ require 'testapps'
8
8
 
9
9
  class ApplicationTests < Test::Unit::TestCase
10
10
 
11
11
  context 'A Rubikon application\'s class' do
12
12
 
13
13
  setup do
14
- @app = RubikonTestApp.instance
14
+ @app = TestApp.instance
15
15
  end
16
16
 
17
17
  should 'be a singleton' do
18
18
  assert_raise NoMethodError do
19
- RubikonTestApp.new
19
+ TestApp.new
20
20
  end
21
21
  end
22
22
 
23
23
  should 'run it\'s instance for called methods' do
24
- assert_equal @app.run(%w{--object_id}), RubikonTestApp.run(%w{--object_id})
24
+ assert_equal @app.run(%w{object_id}), TestApp.run(%w{object_id})
25
25
  end
26
+
26
27
  end
27
28
 
28
29
  context 'A Rubikon application' do
29
30
 
30
31
  setup do
31
- @app = RubikonTestApp
32
+ @app = TestApp
32
33
  @ostream = StringIO.new
33
34
  @app.set :ostream, @ostream
34
35
  end
35
36
 
36
37
  should 'exit gracefully' do
37
- unknown = '--unknown'
38
38
  @app.set :raise_errors, false
39
39
  begin
40
- @app.run([unknown])
40
+ @app.run(%w{unknown})
41
41
  rescue Exception => e
42
+ assert_instance_of SystemExit, e
43
+ assert_equal 1, e.status
42
44
  end
43
- assert_instance_of SystemExit, e
44
- assert_equal 1, e.status
45
45
  @ostream.rewind
46
46
  assert_equal "Error:\n", @ostream.gets
47
- assert_equal " Unknown argument: #{unknown}\n", @ostream.gets
47
+ assert_equal " Unknown command: unknown\n", @ostream.gets
48
48
  @app.set :raise_errors, true
49
49
  end
50
50
 
51
- should 'run it\'s default action without options' do
52
- result = @app.run([])
53
- assert_equal 1, result.size
54
- assert_equal 'default action', result.first
55
- end
56
-
57
- should 'run with a mandatory option' do
58
- result = @app.run(%w{--required arg})
59
- assert_equal 1, result.size
60
- assert_equal 'required argument was arg', result.first
51
+ should 'run its default command without arguments' do
52
+ assert_equal 'default command', @app.run([])
61
53
  end
62
54
 
63
- should 'not run without a mandatory argument' do
64
- assert_raise MissingArgumentError do
65
- @app.run(%w{--required})
55
+ should 'raise an exception when using an unknown command' do
56
+ assert_raise UnknownCommandError do
57
+ @app.run(%w{unknown})
66
58
  end
67
59
  end
68
60
 
69
- should 'require an argument type if it has been defined' do
70
- assert_raise ArgumentTypeError do
71
- @app.run(['--output', 6])
72
- end
73
- assert_raise ArgumentTypeError do
74
- @app.run(['--number_string', 6, 7])
75
- end
76
- assert_raise ArgumentTypeError do
77
- @app.run(['--number_string', 'test' , 6])
78
- end
79
- end
80
-
81
- should 'raise an exception when calling an action with the wrong number of
82
- arguments' do
83
- assert_raise MissingArgumentError do
84
- @app.run(%w{--output})
85
- end
86
- assert_raise ArgumentError do
87
- @app.run(%w{--output}, 'test', 3)
88
- end
89
- end
90
-
91
- should 'raise an exception when using an unknown option' do
92
- assert_raise UnknownOptionError do
93
- @app.run(%w{--unknown})
94
- end
95
- assert_raise UnknownOptionError do
96
- @app.run(%w{--noarg --unknown})
97
- end
98
- assert_raise UnknownOptionError do
99
- @app.run(%w{--unknown --noarg})
61
+ should 'raise an exception when run without arguments without default' do
62
+ assert_raise NoDefaultCommandError do
63
+ TestAppWithoutDefault.run([])
100
64
  end
101
65
  end
102
66
 
@@ -107,31 +71,54 @@ class ApplicationTests < Test::Unit::TestCase
107
71
  input_string = 'test'
108
72
  @istream.puts input_string
109
73
  @istream.rewind
110
- assert_equal [input_string], @app.run(%w{--input})
74
+ assert_equal input_string, @app.run(%w{input})
111
75
  @ostream.rewind
112
76
  assert_equal 'input: ', @ostream.gets
113
77
  end
114
78
 
115
- should 'write output to the user given output stream' do
116
- input_string = 'test'
117
- @app.run(['--output', input_string])
79
+ should "not break output while displaying a throbber or progress bar" do
80
+ @app.run(%w{throbber})
81
+ assert_equal " \b-\b\\\b|\b/\bdon't\nbreak\n", @ostream.string
118
82
  @ostream.rewind
119
- assert_equal "#{input_string}\n", @ostream.gets
120
- assert_equal "#{input_string}#{input_string[0].chr}", @ostream.gets
83
+
84
+ @app.run(%w{progressbar})
85
+ assert_equal "#" * 20 << "\n" << "test\n" * 4, @ostream.string
121
86
  end
122
87
 
123
- should 'provide a throbber' do
124
- @app.run(%w{--throbber})
125
- @ostream.rewind
126
- assert_equal " \b-\b\\\b|\b/\b", @ostream.string
127
- @app.run(%w{--throbber true})
128
- @ostream.rewind
129
- assert_equal " \b-\b\\\b|\b/\bdon't\nbreak\n", @ostream.string
88
+ should 'have working command aliases' do
89
+ assert_equal @app.run(%w{alias_before}), @app.run(%w{object_id})
90
+ assert_equal @app.run(%w{alias_after}), @app.run(%w{object_id})
91
+ end
92
+
93
+ should 'have a global debug flag' do
94
+ @app.run(%w{--debug})
95
+ assert $DEBUG
96
+ $DEBUG = false
97
+ @app.run(%w{-d})
98
+ assert $DEBUG
99
+ $DEBUG = false
100
+ end
101
+
102
+ should 'have a global verbose flag' do
103
+ @app.run(%w{--verbose})
104
+ assert $VERBOSE
105
+ $VERBOSE = false
106
+ @app.run(%w{-v})
107
+ assert $VERBOSE
108
+ $VERBOSE = false
109
+ end
110
+
111
+ should 'have a working help command' do
112
+ @app.run(%w{help})
113
+ assert_match /Usage: [^ ]* \[--debug\|-d\] \[--gopt\|--go \.\.\.\] \[--verbose\|-v\] command \[args\]\n\nCommands:\n help Display this help screen\n input \n object_id \n parameters \n progressbar \n throbber \n/, @ostream.string
130
114
  end
131
115
 
132
- should 'have working action aliases' do
133
- assert_equal @app.run(%w{--alias_before}), @app.run(%w{--object_id})
134
- assert_equal @app.run(%w{--alias_after}), @app.run(%w{--object_id})
116
+ should 'have a working DSL for command parameters' do
117
+ params = @app.run(%w{parameters}).values.uniq.sort { |a,b| a.name.to_s <=> b.name.to_s }
118
+ assert_equal :flag, params[0].name
119
+ assert_equal [:f], params[0].aliases
120
+ assert_equal :option, params[1].name
121
+ assert_equal [:o], params[1].aliases
135
122
  end
136
123
 
137
124
  end