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 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