rubikon 0.2.1 → 0.3.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.
@@ -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