rubikon 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +13 -10
- data/Rakefile +4 -23
- data/gemspec.yml +17 -0
- data/lib/core_ext/enumerable.rb +27 -0
- data/lib/rubikon.rb +3 -5
- data/lib/rubikon/application/dsl_methods.rb +139 -40
- data/lib/rubikon/application/instance_methods.rb +149 -99
- data/lib/rubikon/application/sandbox.rb +0 -0
- data/lib/rubikon/argument_vector.rb +119 -0
- data/lib/rubikon/command.rb +88 -55
- data/lib/rubikon/config/auto_provider.rb +28 -3
- data/lib/rubikon/config/factory.rb +16 -3
- data/lib/rubikon/config/ini_provider.rb +29 -1
- data/lib/rubikon/config/yaml_provider.rb +16 -1
- data/lib/rubikon/{exceptions.rb → errors.rb} +25 -1
- data/lib/rubikon/has_arguments.rb +140 -40
- data/lib/rubikon/parameter.rb +4 -0
- data/lib/rubikon/version.rb +11 -0
- data/samples/config/global/config.yml +4 -0
- data/samples/config/local/config.yml +4 -0
- data/samples/helloworld/hello_world.rb +14 -10
- data/test/config/0/config.yml +2 -0
- data/test/config/1/config.yml +3 -0
- data/test/config/2/config.yml +3 -0
- data/test/config/test.ini +10 -0
- data/test/{test_helper.rb → helper.rb} +0 -4
- data/test/test_application.rb +37 -35
- data/test/test_argument_vector.rb +156 -0
- data/test/test_command.rb +1 -35
- data/test/test_config.rb +1 -1
- data/test/test_has_arguments.rb +161 -20
- data/test/test_ini_provider.rb +1 -1
- data/test/test_parameter.rb +1 -1
- data/test/test_progress_bar.rb +1 -1
- data/test/test_throbber.rb +1 -1
- data/test/testapps.rb +10 -5
- metadata +73 -69
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -73,13 +73,14 @@ arguments.
|
|
73
73
|
Flags and options are easily added to your application's commands using
|
74
74
|
Rubikon's DSL:
|
75
75
|
|
76
|
-
flag :more
|
77
|
-
option :name,
|
78
|
-
command :hello do
|
76
|
+
flag :more, 'A flag'
|
77
|
+
option :name, 'An option', :who
|
78
|
+
command :hello, 'A command' do
|
79
79
|
puts "Hello #{who}"
|
80
80
|
end
|
81
81
|
|
82
|
-
Please see the
|
82
|
+
Please see the [Wiki][5] for more information on the DSL or the `samples`
|
83
|
+
directory for more in detail sample applications.
|
83
84
|
|
84
85
|
**Warning**:
|
85
86
|
|
@@ -95,13 +96,14 @@ section if you want to help to make Rubikon better.
|
|
95
96
|
* Built-in methods to display progress bars and throbbers
|
96
97
|
* Built-in support for configuration files
|
97
98
|
* Built-in support for colored output
|
98
|
-
* Automatic generation of
|
99
|
+
* Automatic generation of application and command help screens
|
100
|
+
* User defined validation of option arguments
|
99
101
|
|
100
102
|
## Future plans
|
101
103
|
|
102
|
-
* User defined type safety of option arguments
|
103
104
|
* Improved error handling
|
104
|
-
*
|
105
|
+
* General optimizations
|
106
|
+
* Improved tests
|
105
107
|
|
106
108
|
## Requirements
|
107
109
|
|
@@ -149,7 +151,8 @@ LICENSE file.
|
|
149
151
|
|
150
152
|
Follow Rubikon on Twitter [@rubikonrb](http://twitter.com/rubikonrb).
|
151
153
|
|
152
|
-
[1]:
|
153
|
-
[2]:
|
154
|
+
[1]: https://github.com/koraktor/rubikon
|
155
|
+
[2]: https://github.com/koraktor/rubikon/issues
|
154
156
|
[3]: http://koraktor.github.com/rubikon
|
155
|
-
[4]:
|
157
|
+
[4]: https://github.com/koraktor/rubikon/wiki/Compatibility
|
158
|
+
[5]: https://github.com/koraktor/rubikon/wiki
|
data/Rakefile
CHANGED
@@ -19,31 +19,12 @@ Rake::TestTask.new do |t|
|
|
19
19
|
end
|
20
20
|
|
21
21
|
begin
|
22
|
-
|
22
|
+
gem 'ore-tasks', '~> 0.3.0'
|
23
|
+
require 'ore/tasks'
|
23
24
|
|
24
|
-
|
25
|
-
line = File.read('lib/rubikon.rb')[/^\s*VERSION\s*=\s*.*/]
|
26
|
-
gem.version = line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
27
|
-
end
|
28
|
-
|
29
|
-
# Gem specification
|
30
|
-
Jeweler::Tasks.new(gemspec) do |gem|
|
31
|
-
gem.authors = ['Sebastian Staudt']
|
32
|
-
gem.email = 'koraktor@gmail.com'
|
33
|
-
gem.description = 'A simple to use, yet powerful Ruby framework for building console-based applications.'
|
34
|
-
gem.date = Time.now
|
35
|
-
gem.files = %w(.yardopts README.md Rakefile LICENSE) + samples_files + src_files + test_files
|
36
|
-
gem.has_rdoc = false
|
37
|
-
gem.homepage = 'http://koraktor.github.com/rubikon'
|
38
|
-
gem.name = gem.rubyforge_project = 'rubikon'
|
39
|
-
gem.summary = 'Rubikon - A Ruby console app framework'
|
40
|
-
|
41
|
-
gem.add_development_dependency('jeweler')
|
42
|
-
gem.add_development_dependency('shoulda')
|
43
|
-
gem.add_development_dependency('yard')
|
44
|
-
end
|
25
|
+
Ore::Tasks.new
|
45
26
|
rescue LoadError
|
46
|
-
puts
|
27
|
+
puts "Run `gem install ore-tasks` to install 'ore/tasks'."
|
47
28
|
end
|
48
29
|
|
49
30
|
begin
|
data/gemspec.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
name: rubikon
|
2
|
+
summary: Rubikon - A Ruby console app framework
|
3
|
+
description: A simple to use, yet powerful Ruby framework for building
|
4
|
+
console-based applications.
|
5
|
+
|
6
|
+
license: BSD
|
7
|
+
authors: Sebastian Staudt
|
8
|
+
email: koraktor@gmail.com
|
9
|
+
homepage: http://koraktor.github.com/rubikon
|
10
|
+
has_yard: true
|
11
|
+
|
12
|
+
require_paths: lib
|
13
|
+
|
14
|
+
development_dependencies:
|
15
|
+
shoulda: ~> 2.11.3
|
16
|
+
ore-tasks: ~> 0.3.0
|
17
|
+
yard: ~> 0.6.4
|
@@ -0,0 +1,27 @@
|
|
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
|
+
unless Enumerable.method_defined?(:max_by)
|
7
|
+
|
8
|
+
# Extends Ruby's own Enumrable module with method #max_by? for Ruby < 1.8.7
|
9
|
+
#
|
10
|
+
# @author Sebastian Staudt
|
11
|
+
# @since 0.6.0
|
12
|
+
module Enumerable
|
13
|
+
|
14
|
+
# Returns the object in enum that gives the maximum value from the given
|
15
|
+
# block.
|
16
|
+
#
|
17
|
+
# @yield [obj] The block to call on each element in the enum
|
18
|
+
# @yieldparam [Object] obj A single object in the enum
|
19
|
+
# @yieldreturn [Comparable] A value that can be compared (+<=>+) with the
|
20
|
+
# values of the other objects in the enum
|
21
|
+
def max_by(&block)
|
22
|
+
max { |a , b| block.call(a) <=> block.call(b) }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/rubikon.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under
|
2
2
|
# the terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2009-
|
4
|
+
# Copyright (c) 2009-2011, Sebastian Staudt
|
5
5
|
|
6
6
|
libdir = File.dirname(__FILE__)
|
7
7
|
$:.unshift(libdir) unless $:.include?(libdir)
|
8
8
|
|
9
|
+
require 'core_ext/enumerable'
|
9
10
|
require 'core_ext/object'
|
10
11
|
require 'core_ext/string'
|
11
12
|
require 'rubikon/application/base'
|
13
|
+
require 'rubikon/version'
|
12
14
|
|
13
15
|
# Rubikon is a simple to use, yet powerful Ruby framework for building
|
14
16
|
# console-based applications. Rubikon aims to provide an easy to write and easy
|
@@ -22,8 +24,4 @@ require 'rubikon/application/base'
|
|
22
24
|
# @author Sebastian Staudt
|
23
25
|
# @since 0.1.0
|
24
26
|
module Rubikon
|
25
|
-
|
26
|
-
# This is the current version of the Rubikon gem
|
27
|
-
VERSION = '0.5.3'
|
28
|
-
|
29
27
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
# This code is free software; you can redistribute it and/or modify it under
|
2
2
|
# the terms of the new BSD License.
|
3
3
|
#
|
4
|
-
# Copyright (c) 2010, Sebastian Staudt
|
4
|
+
# Copyright (c) 2010-2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'rubikon/argument_vector'
|
5
7
|
|
6
8
|
module Rubikon
|
7
9
|
|
@@ -53,12 +55,22 @@ module Rubikon
|
|
53
55
|
#
|
54
56
|
# @param [Symbol] command_name The name of the command to call
|
55
57
|
# @param [Array<String>] args The arguments to pass to the called command
|
58
|
+
# @see ArgumentVector#params!
|
56
59
|
# @see Command#run
|
57
60
|
def call(command_name, *args)
|
58
|
-
|
61
|
+
args.extend ArgumentVector
|
62
|
+
current_command = @current_command
|
63
|
+
current_param = @current_param
|
64
|
+
|
59
65
|
@current_command = @commands[command_name]
|
60
|
-
@current_command.
|
61
|
-
|
66
|
+
args.params!(@current_command.params).each do |param|
|
67
|
+
@current_param = param
|
68
|
+
param.send :active!
|
69
|
+
@current_param = nil
|
70
|
+
end
|
71
|
+
@current_command.send :run
|
72
|
+
@current_command = current_command
|
73
|
+
@current_param = current_param
|
62
74
|
end
|
63
75
|
|
64
76
|
# Define a new application Command or an alias to an existing one
|
@@ -67,8 +79,7 @@ module Rubikon
|
|
67
79
|
# application parameters. This might also be a Hash where every
|
68
80
|
# key will be an alias to the corresponding value, e.g. <tt>{
|
69
81
|
# :alias => :command }</tt>.
|
70
|
-
# @param
|
71
|
-
# the application's help screen
|
82
|
+
# @param options (see HasArguments#initialize)
|
72
83
|
# @param [Proc] block A block that contains the code that should be
|
73
84
|
# executed when this Command is called, i.e. when the application
|
74
85
|
# is called with the associated parameter
|
@@ -77,7 +88,7 @@ module Rubikon
|
|
77
88
|
# @see default
|
78
89
|
# @see Command
|
79
90
|
# @since 0.2.0
|
80
|
-
def command(name,
|
91
|
+
def command(name, *options, &block)
|
81
92
|
command = nil
|
82
93
|
|
83
94
|
if name.is_a? Hash
|
@@ -91,8 +102,7 @@ module Rubikon
|
|
91
102
|
end
|
92
103
|
end
|
93
104
|
else
|
94
|
-
command = Command.new(self, name,
|
95
|
-
command.description = description unless description.nil?
|
105
|
+
command = Command.new(self, name, *options, &block)
|
96
106
|
@commands.each do |command_alias, command_name|
|
97
107
|
if command_name == command.name
|
98
108
|
@commands[command_alias] = command
|
@@ -123,8 +133,7 @@ module Rubikon
|
|
123
133
|
# Define the default Command of the application, i.e. the Command that is
|
124
134
|
# called if no matching Command parameter can be found
|
125
135
|
#
|
126
|
-
# @param
|
127
|
-
# the application's help screen
|
136
|
+
# @param options (see HasArguments#initialize)
|
128
137
|
# @param [Proc] block A block that contains the code that should be
|
129
138
|
# executed when this Command is called, i.e. when no command
|
130
139
|
# parameter is given to the application
|
@@ -133,12 +142,54 @@ module Rubikon
|
|
133
142
|
# @see Command
|
134
143
|
# @see command
|
135
144
|
# @since 0.2.0
|
136
|
-
|
137
|
-
|
138
|
-
|
145
|
+
#
|
146
|
+
# @example Define a default command with an argument
|
147
|
+
# default 'This is the default', :arg do
|
148
|
+
# ...
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# @example Use another command as default
|
152
|
+
# default :other_command
|
153
|
+
def default(*options, &block)
|
154
|
+
if options.size == 1 && options.first.is_a?(Symbol) && !block_given?
|
155
|
+
command :__default => options.first
|
139
156
|
else
|
140
|
-
command
|
157
|
+
command :__default, *options, &block
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Set the default configuration for this application
|
162
|
+
#
|
163
|
+
# @param [Hash] config The default configuration to use
|
164
|
+
# @see #config
|
165
|
+
# @since 0.6.0
|
166
|
+
def default_config=(config)
|
167
|
+
unless config.is_a? Hash
|
168
|
+
raise ArgumentError.new('Configuration has to be a Hash')
|
141
169
|
end
|
170
|
+
|
171
|
+
@default_config = config
|
172
|
+
end
|
173
|
+
|
174
|
+
# Output a line of text using +IO#puts+ of the error output stream
|
175
|
+
#
|
176
|
+
# @param [String] text The text to write into the error output stream
|
177
|
+
# @since 0.6.0
|
178
|
+
def error(text = nil)
|
179
|
+
estream.puts text
|
180
|
+
end
|
181
|
+
|
182
|
+
# Convenience method for accessing the user-defined error output stream
|
183
|
+
#
|
184
|
+
# Use this if you want to work directly with the error output stream
|
185
|
+
#
|
186
|
+
# @return [IO] The error output stream object - usually +$stderr+
|
187
|
+
# @since 0.6.0
|
188
|
+
#
|
189
|
+
# @example
|
190
|
+
# estream.flush
|
191
|
+
def estream
|
192
|
+
@settings[:estream]
|
142
193
|
end
|
143
194
|
|
144
195
|
# Create a new Flag with the given name for the next Command
|
@@ -158,11 +209,13 @@ module Rubikon
|
|
158
209
|
# command :something do
|
159
210
|
# ...
|
160
211
|
# end
|
161
|
-
def flag(name, &block)
|
212
|
+
def flag(name, description = nil, &block)
|
162
213
|
if name.is_a? Hash
|
163
214
|
@parameters << name
|
164
215
|
else
|
165
|
-
|
216
|
+
flag = Flag.new(self, name, &block)
|
217
|
+
flag.description = description unless description.nil?
|
218
|
+
@parameters << flag
|
166
219
|
end
|
167
220
|
end
|
168
221
|
|
@@ -184,7 +237,7 @@ module Rubikon
|
|
184
237
|
# end
|
185
238
|
# @example Define an alias to a global flag
|
186
239
|
# global_flag :q => :quiet
|
187
|
-
def global_flag(name, &block)
|
240
|
+
def global_flag(name, description = nil, &block)
|
188
241
|
if name.is_a? Hash
|
189
242
|
name.each do |alias_name, flag_name|
|
190
243
|
flag = @global_parameters[flag_name]
|
@@ -197,6 +250,7 @@ module Rubikon
|
|
197
250
|
end
|
198
251
|
else
|
199
252
|
flag = Flag.new(self, name, &block)
|
253
|
+
flag.description = description unless description.nil?
|
200
254
|
@global_parameters.each do |flag_alias, flag_name|
|
201
255
|
if flag_name == flag.name
|
202
256
|
@global_parameters[flag_alias] = flag
|
@@ -217,15 +271,15 @@ module Rubikon
|
|
217
271
|
# @see Option
|
218
272
|
# @since 0.2.0
|
219
273
|
#
|
220
|
-
# @example Define a global option
|
221
|
-
# global_option :user,
|
222
|
-
# @example Define a global option with a block to execute
|
223
|
-
# global_option :user,
|
224
|
-
# @user =
|
274
|
+
# @example Define a global option with an optional argument
|
275
|
+
# global_option :user, :login => :optional
|
276
|
+
# @example Define a global option with an argument and a block to execute
|
277
|
+
# global_option :user, :login do
|
278
|
+
# @user = login
|
225
279
|
# end
|
226
280
|
# @example Define an alias to a global option
|
227
281
|
# global_option :u => :user
|
228
|
-
def global_option(name,
|
282
|
+
def global_option(name, *options, &block)
|
229
283
|
if name.is_a? Hash
|
230
284
|
name.each do |alias_name, option_name|
|
231
285
|
option = @global_parameters[option_name]
|
@@ -237,7 +291,7 @@ module Rubikon
|
|
237
291
|
end
|
238
292
|
end
|
239
293
|
else
|
240
|
-
option = Option.new(self, name,
|
294
|
+
option = Option.new(self, name, *options, &block)
|
241
295
|
@global_parameters.each do |option_alias, option_name|
|
242
296
|
if option_name == option.name
|
243
297
|
@global_parameters[option_alias] = option
|
@@ -252,6 +306,9 @@ module Rubikon
|
|
252
306
|
#
|
253
307
|
# @param [String, #to_s] prompt A String or other Object responding to
|
254
308
|
# +to_s+ used for displaying a prompt to the user
|
309
|
+
# @param [Array<String>] expected A list of strings that are accepted as
|
310
|
+
# valid input. If not empty, input will be checked and the prompt
|
311
|
+
# will be repeated if required.
|
255
312
|
# @since 0.2.0
|
256
313
|
#
|
257
314
|
# @example Display a prompt "Please type something: "
|
@@ -261,11 +318,20 @@ module Rubikon
|
|
261
318
|
# # Do something with the data
|
262
319
|
# ...
|
263
320
|
# end
|
264
|
-
|
265
|
-
|
266
|
-
|
321
|
+
#
|
322
|
+
# @example Display a question with validated input
|
323
|
+
# command :question do
|
324
|
+
# good = input 'Do you feel good', 'y', 'n'
|
325
|
+
# ...
|
326
|
+
# end
|
327
|
+
def input(prompt = '', *expected)
|
328
|
+
prompt << " [#{expected.join '/'}]" unless expected.empty?
|
329
|
+
ostream << "#{prompt}: " unless prompt.to_s.empty?
|
330
|
+
input = @settings[:istream].gets[0..-2]
|
331
|
+
unless expected.empty? || expected.include?(input)
|
332
|
+
input = input 'Please provide valid input', *expected
|
267
333
|
end
|
268
|
-
|
334
|
+
input
|
269
335
|
end
|
270
336
|
|
271
337
|
# Create a new Option with the given name for the next Command
|
@@ -275,25 +341,24 @@ module Rubikon
|
|
275
341
|
# options, +--+ for other options). This might also be a Hash
|
276
342
|
# where every key will be an alias to the corresponding value,
|
277
343
|
# e.g. <tt>{ :alias => :option }</tt>.
|
278
|
-
# @param
|
279
|
-
# Use +0+ for no required arguments or a negative value for an
|
280
|
-
# arbitrary number of arguments
|
344
|
+
# @param options (see HasArguments#initialize)
|
281
345
|
# @param [Proc] block An optional code block that should be executed if
|
282
346
|
# this option is used
|
283
347
|
# @see Option
|
284
348
|
# @since 0.2.0
|
285
349
|
#
|
286
|
-
# @example
|
287
|
-
# option :message,
|
350
|
+
# @example Define an option (and its alias) to a command
|
351
|
+
# option :message, 'A message', :text
|
288
352
|
# option :m => :message
|
289
353
|
# command :something do
|
290
|
-
#
|
354
|
+
# puts message.text
|
291
355
|
# end
|
292
|
-
def option(name,
|
356
|
+
def option(name, *options, &block)
|
293
357
|
if name.is_a? Hash
|
294
358
|
@parameters << name
|
295
359
|
else
|
296
|
-
|
360
|
+
option = Option.new(self, name.to_s, *options, &block)
|
361
|
+
@parameters << option
|
297
362
|
end
|
298
363
|
end
|
299
364
|
|
@@ -423,6 +488,7 @@ module Rubikon
|
|
423
488
|
# +colors+:: If +true+, enables colored output using ColoredIO
|
424
489
|
# +config_file+:: The name of the config file to search
|
425
490
|
# +config_paths+:: The paths to search for config files
|
491
|
+
# +estream+:: Defines an error output stream to use
|
426
492
|
# +help_banner+:: Defines a banner for the help message
|
427
493
|
# +istream+:: Defines an input stream to use
|
428
494
|
# +name+:: Defines the name of the application
|
@@ -434,13 +500,46 @@ module Rubikon
|
|
434
500
|
# set :autorun, false
|
435
501
|
def set(setting, value)
|
436
502
|
setting = setting.to_sym
|
437
|
-
|
438
|
-
|
439
|
-
|
503
|
+
if setting == :estream
|
504
|
+
self.estream = value
|
505
|
+
elsif setting == :ostream
|
440
506
|
self.ostream = value
|
507
|
+
else
|
508
|
+
@settings[setting.to_sym] = value
|
441
509
|
end
|
442
510
|
end
|
443
511
|
|
512
|
+
# Saves the current configuration into a configuration file
|
513
|
+
#
|
514
|
+
# The file name and format are specified in the application settings.
|
515
|
+
#
|
516
|
+
# @param [:global, :local, :user, String] Either one of the default
|
517
|
+
# scopes or a specific path. The scopes map to a special path. On
|
518
|
+
# UNIX systems this is +/etc+ for global configurations, the
|
519
|
+
# user's home directory (+~+) for user configurations and the
|
520
|
+
# current working directory (+.+) for local configurations.
|
521
|
+
# @see #default_config=
|
522
|
+
# @since 0.6.0
|
523
|
+
def save_config(scope = :user)
|
524
|
+
case scope
|
525
|
+
when :global
|
526
|
+
if RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
|
527
|
+
path = ENV['ALLUSERSPROFILE']
|
528
|
+
else
|
529
|
+
path = '/etc'
|
530
|
+
end
|
531
|
+
when :local
|
532
|
+
path = File.expand_path '.'
|
533
|
+
when :user
|
534
|
+
path = File.expand_path '~'
|
535
|
+
else
|
536
|
+
path = scope
|
537
|
+
end
|
538
|
+
|
539
|
+
@config_factory.save_config @config,
|
540
|
+
File.join(path, @settings[:config_file])
|
541
|
+
end
|
542
|
+
|
444
543
|
# Displays a throbber while the given block is executed
|
445
544
|
#
|
446
545
|
# @param [Proc] block The block to execute while the throbber is
|