rubikon 0.5.3 → 0.6.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.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009, Sebastian Staudt
1
+ Copyright (c) 2009-2011, Sebastian Staudt
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification,
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, [:who]
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 `samples` directory for more in detail sample applications.
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 a application help screen
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
- * Automatic generation of command help screens
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]: http://github.com/koraktor/rubikon
153
- [2]: http://github.com/koraktor/rubikon/issues
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]: http://github.com/koraktor/rubikon/wiki/Compatibility
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
- require 'jeweler'
22
+ gem 'ore-tasks', '~> 0.3.0'
23
+ require 'ore/tasks'
23
24
 
24
- gemspec = Gem::Specification.new do |gem|
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 'You need Jeweler to build the gem. Install it using `gem install jeweler`.'
27
+ puts "Run `gem install ore-tasks` to install 'ore/tasks'."
47
28
  end
48
29
 
49
30
  begin
@@ -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
@@ -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-2010, Sebastian Staudt
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
- command = @current_command
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.send(:run, *args)
61
- @current_command = command
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 [String] description A description for this Command for use in
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, arg_count = nil, description = nil, &block)
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, arg_count, &block)
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 [String] description A description for this Command for use in
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
- def default(arg_count = nil, description = nil, &block)
137
- if arg_count.is_a? Symbol
138
- command({ :__default => arg_count })
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(:__default, arg_count, description, &block)
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
- @parameters << Flag.new(self, name, &block)
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, 1
222
- # @example Define a global option with a block to execute
223
- # global_option :user, 1 do
224
- # @user = args[0]
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, arg_count = 0, &block)
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, arg_count, &block)
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
- def input(prompt = '')
265
- unless prompt.to_s.empty?
266
- ostream << "#{prompt}: "
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
- @settings[:istream].gets[0..-2]
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 [Numeric] arg_count The number of arguments this option takes.
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,1
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, arg_count = 0, &block)
356
+ def option(name, *options, &block)
293
357
  if name.is_a? Hash
294
358
  @parameters << name
295
359
  else
296
- @parameters << Option.new(self, name.to_s, arg_count, &block)
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
- unless setting == :ostream
438
- @settings[setting.to_sym] = value
439
- else
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