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.
- data/LICENSE +0 -0
- data/README.md +39 -16
- data/Rakefile +29 -16
- data/lib/core_ext/string.rb +16 -0
- data/lib/rubikon/application/base.rb +10 -10
- data/lib/rubikon/application/class_methods.rb +19 -31
- data/lib/rubikon/application/dsl_methods.rb +427 -0
- data/lib/rubikon/application/instance_methods.rb +160 -261
- data/lib/rubikon/command.rb +135 -0
- data/lib/rubikon/exceptions.rb +62 -9
- data/lib/rubikon/flag.rb +56 -0
- data/lib/rubikon/option.rb +30 -0
- data/lib/rubikon/parameter.rb +101 -0
- data/lib/rubikon/progress_bar.rb +23 -12
- data/lib/rubikon/throbber.rb +10 -5
- data/lib/rubikon.rb +14 -3
- data/test/application_tests.rb +60 -73
- data/test/command_tests.rb +102 -0
- data/test/commands/external_command.rb +1 -0
- data/test/flag_tests.rb +40 -0
- data/test/option_tests.rb +75 -0
- data/test/progress_bar_tests.rb +13 -3
- data/test/test_helper.rb +3 -3
- data/test/testapps.rb +64 -0
- data/test/throbber_tests.rb +25 -3
- metadata +64 -19
- data/VERSION.yml +0 -4
- data/lib/rubikon/action.rb +0 -74
- data/test/action_tests.rb +0 -36
- data/test/testapp.rb +0 -64
@@ -0,0 +1,427 @@
|
|
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
|
+
module Application
|
9
|
+
|
10
|
+
# This module contains all DSL-related instance methods of
|
11
|
+
# +Application::Base+ and its subclasses. The methods of this module may be
|
12
|
+
# used to define and enhance a Rubikon application.
|
13
|
+
#
|
14
|
+
# @author Sebastian Staudt
|
15
|
+
# @see Application::Base
|
16
|
+
# @since 0.3.0
|
17
|
+
module DSLMethods
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Returns the arguments for the currently executed Command
|
22
|
+
#
|
23
|
+
# @return [Array]
|
24
|
+
# @since 0.2.0
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# command :something do
|
28
|
+
# puts arguments[0]
|
29
|
+
# end
|
30
|
+
def args
|
31
|
+
unless @current_command.nil?
|
32
|
+
@current_command.arguments
|
33
|
+
else
|
34
|
+
@current_global_option.arguments
|
35
|
+
end
|
36
|
+
end
|
37
|
+
alias_method :arguments, :args
|
38
|
+
|
39
|
+
# Define a new application Command or an alias to an existing one
|
40
|
+
#
|
41
|
+
# @param [String, Hash] name The name of the Command as used in
|
42
|
+
# application parameters. This might also be a Hash where every
|
43
|
+
# key will be an alias to the corresponding value, e.g. <tt>{
|
44
|
+
# :alias => :command }</tt>.
|
45
|
+
# @param [String] description A description for this Command for use in
|
46
|
+
# the application's help screen
|
47
|
+
# @param [Proc] block A block that contains the code that should be
|
48
|
+
# executed when this Command is called, i.e. when the application
|
49
|
+
# is called with the associated parameter
|
50
|
+
#
|
51
|
+
# @return [Command]
|
52
|
+
# @since 0.2.0
|
53
|
+
def command(name, description = nil, &block)
|
54
|
+
if name.is_a? Hash
|
55
|
+
name.each do |alias_name, command_name|
|
56
|
+
command = @commands[command_name]
|
57
|
+
if command.nil?
|
58
|
+
@commands[alias_name] = command_name
|
59
|
+
else
|
60
|
+
command.aliases << alias_name
|
61
|
+
@commands[alias_name] = command
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
command = Command.new(self, name, &block)
|
66
|
+
command.description = description unless description.nil?
|
67
|
+
@commands.each do |command_alias, command_name|
|
68
|
+
if command_name == command.name
|
69
|
+
@commands[command_alias] = command
|
70
|
+
command.aliases << command_alias
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@commands[command.name] = command
|
74
|
+
end
|
75
|
+
|
76
|
+
unless command.nil? || @parameters.empty?
|
77
|
+
@parameters.each do |parameter|
|
78
|
+
command << parameter
|
79
|
+
end
|
80
|
+
@parameters.clear
|
81
|
+
end
|
82
|
+
|
83
|
+
command
|
84
|
+
end
|
85
|
+
|
86
|
+
# Prints a debug message if <tt>$DEBUG</tt> is +true+, e.g. if the user
|
87
|
+
# supplied the <tt>--debug</tt> (<tt>-d</tt>) flag.
|
88
|
+
#
|
89
|
+
# @since 0.2.0
|
90
|
+
def debug(message)
|
91
|
+
ostream.puts message if $DEBUG
|
92
|
+
end
|
93
|
+
|
94
|
+
# Define the default Command of the application, i.e. the Command that is
|
95
|
+
# called if no matching Command parameter can be found
|
96
|
+
#
|
97
|
+
# @param [String] description A description for this Command for use in
|
98
|
+
# the application's help screen
|
99
|
+
# @param [Proc] block A block that contains the code that should be
|
100
|
+
# executed when this Command is called, i.e. when no command
|
101
|
+
# parameter is given to the application
|
102
|
+
#
|
103
|
+
# @return [Command] The default Command object
|
104
|
+
# @since 0.2.0
|
105
|
+
def default(description = nil, &block)
|
106
|
+
if description.is_a? Symbol
|
107
|
+
command({ :__default => description })
|
108
|
+
else
|
109
|
+
command(:__default, description, &block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create a new Flag with the given name for the next Command
|
114
|
+
#
|
115
|
+
# @param [Symbol, #to_sym] name The name of the flag (without dashes).
|
116
|
+
# Dashes will be automatically added (<tt>-</tt> for
|
117
|
+
# single-character flags, <tt>--</tt> for other flags). This might
|
118
|
+
# also be a Hash where every key will be an alias to the
|
119
|
+
# corresponding value, e.g. <tt>{ :alias => :flag }</tt>.
|
120
|
+
# @param [Proc] block An optional code block that should be executed if
|
121
|
+
# this flag is used
|
122
|
+
# @since 0.2.0
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# flag :status
|
126
|
+
# flag :st => :status
|
127
|
+
# command :something do
|
128
|
+
# ...
|
129
|
+
# end
|
130
|
+
def flag(name, &block)
|
131
|
+
if name.is_a? Hash
|
132
|
+
@parameters << name
|
133
|
+
else
|
134
|
+
@parameters << Flag.new(name, &block)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Checks whether parameter with the given name has been supplied by the
|
139
|
+
# user on the command-line.
|
140
|
+
#
|
141
|
+
# @param [#to_sym] name The name of the parameter to check
|
142
|
+
# @since 0.2.0
|
143
|
+
#
|
144
|
+
# @example
|
145
|
+
# flag :status
|
146
|
+
# command :something do
|
147
|
+
# print_status if given? :status
|
148
|
+
# end
|
149
|
+
def given?(name)
|
150
|
+
name = name.to_sym
|
151
|
+
parameter = @global_parameters[name]
|
152
|
+
parameter = @current_command.parameters[name] if parameter.nil?
|
153
|
+
return false if parameter.nil?
|
154
|
+
parameter.active?
|
155
|
+
end
|
156
|
+
|
157
|
+
# Create a new flag with the given name to be used globally
|
158
|
+
#
|
159
|
+
# Global flags are not bound to any command and can therefore be used
|
160
|
+
# throughout the application with the same result.
|
161
|
+
#
|
162
|
+
# @param (see #flag)
|
163
|
+
# @see #flag
|
164
|
+
# @see Flag
|
165
|
+
# @since 0.2.0
|
166
|
+
#
|
167
|
+
# @example Define a global flag
|
168
|
+
# global_flag :quiet
|
169
|
+
# @example Define a global flag with a block to execute
|
170
|
+
# global_flag :quiet do
|
171
|
+
# @quiet = true
|
172
|
+
# end
|
173
|
+
# @example Define an alias to a global flag
|
174
|
+
# global_flag :q => :quiet
|
175
|
+
def global_flag(name, &block)
|
176
|
+
if name.is_a? Hash
|
177
|
+
name.each do |alias_name, flag_name|
|
178
|
+
flag = @global_parameters[flag_name]
|
179
|
+
if flag.nil?
|
180
|
+
@global_parameters[alias_name] = flag_name
|
181
|
+
else
|
182
|
+
flag.aliases << alias_name
|
183
|
+
@global_parameters[alias_name] = flag
|
184
|
+
end
|
185
|
+
end
|
186
|
+
else
|
187
|
+
flag = Flag.new(name, &block)
|
188
|
+
@global_parameters.each do |flag_alias, flag_name|
|
189
|
+
if flag_name == flag.name
|
190
|
+
@global_parameters[flag_alias] = flag
|
191
|
+
flag.aliases << flag_alias
|
192
|
+
end
|
193
|
+
end
|
194
|
+
@global_parameters[flag.name] = flag
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Create a new option with the given name to be used globally
|
199
|
+
#
|
200
|
+
# Global options are not bound to any command and can therefore be used
|
201
|
+
# throughout the application with the same result.
|
202
|
+
#
|
203
|
+
# @param (see #option)
|
204
|
+
# @see #option
|
205
|
+
# @see Option
|
206
|
+
# @since 0.2.0
|
207
|
+
#
|
208
|
+
# @example Define a global option
|
209
|
+
# global_option :user, 1
|
210
|
+
# @example Define a global option with a block to execute
|
211
|
+
# global_option :user, 1 do
|
212
|
+
# @user = args[0]
|
213
|
+
# end
|
214
|
+
# @example Define an alias to a global option
|
215
|
+
# global_option :u => :user
|
216
|
+
def global_option(name, arg_count = 0, &block)
|
217
|
+
if name.is_a? Hash
|
218
|
+
name.each do |alias_name, option_name|
|
219
|
+
option = @global_parameters[option_name]
|
220
|
+
if option.nil?
|
221
|
+
@global_parameters[alias_name] = option_name
|
222
|
+
else
|
223
|
+
option.aliases << alias_name
|
224
|
+
@global_parameters[alias_name] = option
|
225
|
+
end
|
226
|
+
end
|
227
|
+
else
|
228
|
+
option = Option.new(name, arg_count, &block)
|
229
|
+
@global_parameters.each do |option_alias, option_name|
|
230
|
+
if option_name == option.name
|
231
|
+
@global_parameters[option_alias] = option
|
232
|
+
option.aliases << option_alias
|
233
|
+
end
|
234
|
+
end
|
235
|
+
@global_parameters[option.name] = option
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Prompts the user for input
|
240
|
+
#
|
241
|
+
# @param [String, #to_s] prompt A String or other Object responding to
|
242
|
+
# +to_s+ used for displaying a prompt to the user
|
243
|
+
# @since 0.2.0
|
244
|
+
#
|
245
|
+
# @example Display a prompt "Please type something: "
|
246
|
+
# action 'interactive' do
|
247
|
+
# user_provided_value = input 'Please type something'
|
248
|
+
#
|
249
|
+
# # Do something with the data
|
250
|
+
# ...
|
251
|
+
# end
|
252
|
+
def input(prompt = '')
|
253
|
+
unless prompt.to_s.empty?
|
254
|
+
ostream << "#{prompt}: "
|
255
|
+
end
|
256
|
+
@settings[:istream].gets[0..-2]
|
257
|
+
end
|
258
|
+
|
259
|
+
# Create a new Option with the given name for the next Command
|
260
|
+
#
|
261
|
+
# @param [Symbol, #to_sym] name The name of the Option (without dashes).
|
262
|
+
# Dashes will be automatically added (+-+ for single-character
|
263
|
+
# options, +--+ for other options). This might also be a Hash
|
264
|
+
# where every key will be an alias to the corresponding value,
|
265
|
+
# e.g. <tt>{ :alias => :option }</tt>.
|
266
|
+
# @param [Numeric] arg_count The number of arguments this option takes.
|
267
|
+
# Use +0+ for no required arguments or a negative value for an
|
268
|
+
# arbitrary number of arguments
|
269
|
+
# @param [Proc] block An optional code block that should be executed if
|
270
|
+
# this option is used
|
271
|
+
# @since 0.2.0
|
272
|
+
#
|
273
|
+
# @example
|
274
|
+
# option :message,1
|
275
|
+
# option :m => :message
|
276
|
+
# command :something do
|
277
|
+
# ...
|
278
|
+
# end
|
279
|
+
def option(name, arg_count = 0, &block)
|
280
|
+
if name.is_a? Hash
|
281
|
+
@parameters << name
|
282
|
+
else
|
283
|
+
@parameters << Option.new(name.to_s, arg_count, &block)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Convenience method for accessing the user-defined output stream
|
288
|
+
#
|
289
|
+
# Use this if you want to work directly with the output stream
|
290
|
+
#
|
291
|
+
# @return [IO] The output stream object - usually +$stdout+
|
292
|
+
# @since 0.2.0
|
293
|
+
#
|
294
|
+
# @example
|
295
|
+
# ostream.flush
|
296
|
+
def ostream
|
297
|
+
@settings[:ostream]
|
298
|
+
end
|
299
|
+
|
300
|
+
# Returns the parameters for the currently executed command
|
301
|
+
#
|
302
|
+
# @return [Array] The parameters of the currently executed command
|
303
|
+
# @see Command
|
304
|
+
# @since 0.2.0
|
305
|
+
#
|
306
|
+
# @example
|
307
|
+
# option :message, 1
|
308
|
+
# command :something do
|
309
|
+
# puts parameters[:message].args[0] if given? :message
|
310
|
+
# end
|
311
|
+
def params
|
312
|
+
@current_command.parameters
|
313
|
+
end
|
314
|
+
alias_method :parameters, :params
|
315
|
+
|
316
|
+
# Output text using +IO#<<+ of the output stream
|
317
|
+
#
|
318
|
+
# @param [String] text The text to write into the output stream
|
319
|
+
# @since 0.2.0
|
320
|
+
def put(text)
|
321
|
+
@settings[:ostream] << text
|
322
|
+
@settings[:ostream].flush
|
323
|
+
end
|
324
|
+
|
325
|
+
# Output a character using +IO#putc+ of the output stream
|
326
|
+
#
|
327
|
+
# @param [String, Numeric] char The character to write into the output
|
328
|
+
# stream
|
329
|
+
# @since 0.2.0
|
330
|
+
def putc(char)
|
331
|
+
@settings[:ostream].putc char
|
332
|
+
end
|
333
|
+
|
334
|
+
# Output a line of text using +IO#puts+ of the output stream
|
335
|
+
#
|
336
|
+
# @param [String] text The text to write into the output stream
|
337
|
+
# @since 0.2.0
|
338
|
+
def puts(text)
|
339
|
+
@settings[:ostream].puts text
|
340
|
+
end
|
341
|
+
|
342
|
+
# Displays a progress bar while the given block is executed
|
343
|
+
#
|
344
|
+
# Inside the block you have access to a instance of ProgressBar. So you
|
345
|
+
# can update the progress using <tt>ProgressBar#+</tt>.
|
346
|
+
#
|
347
|
+
# @param [Hash] options A Hash of options that should be passed to the
|
348
|
+
# ProgressBar object.
|
349
|
+
# @param [Proc] block The block to execute
|
350
|
+
# @yield [ProgressBar] The given block may be used to change the values
|
351
|
+
# of the progress bar
|
352
|
+
# @yieldparam [ProgressBar] progress The progress bar indicating the
|
353
|
+
# progress of the block
|
354
|
+
#
|
355
|
+
# @example
|
356
|
+
# progress_bar(:maximum => 5) do |progress|
|
357
|
+
# 5.times do |file|
|
358
|
+
# File.read("any#{file}.txt")
|
359
|
+
# progress.+
|
360
|
+
# end
|
361
|
+
# end
|
362
|
+
#
|
363
|
+
# @see ProgressBar
|
364
|
+
# @since 0.2.0
|
365
|
+
def progress_bar(*options, &block)
|
366
|
+
hidden_output do |ostream|
|
367
|
+
options = options[0]
|
368
|
+
options[:ostream] = ostream
|
369
|
+
|
370
|
+
progress = ProgressBar.new(options)
|
371
|
+
|
372
|
+
block.call(progress)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
# Sets an application setting
|
377
|
+
#
|
378
|
+
# @param [Symbol, #to_sym] setting The name of the setting to change
|
379
|
+
# @param [Object] value The value the setting should be changed to
|
380
|
+
# @since 0.2.0
|
381
|
+
#
|
382
|
+
# Available settings
|
383
|
+
# +autorun+:: If true, let the application run as soon as its
|
384
|
+
# class is defined
|
385
|
+
# +help_banner+:: Defines a banner for the help message
|
386
|
+
# (<em>unused</em>)
|
387
|
+
# +istream+:: Defines an input stream to use
|
388
|
+
# +name+:: Defines the name of the application
|
389
|
+
# +ostream+:: Defines an output stream to use
|
390
|
+
# +raise_errors+:: If true, raise errors, otherwise fail gracefully
|
391
|
+
#
|
392
|
+
# @example
|
393
|
+
# set :name, 'My App'
|
394
|
+
# set :autorun, false
|
395
|
+
def set(setting, value)
|
396
|
+
@settings[setting.to_sym] = value
|
397
|
+
end
|
398
|
+
|
399
|
+
# Displays a throbber while the given block is executed
|
400
|
+
#
|
401
|
+
# @param [Proc] block The block to execute while the throbber is
|
402
|
+
# displayed
|
403
|
+
# @since 0.2.0
|
404
|
+
# @yield While the block is executed a throbber is displayed
|
405
|
+
#
|
406
|
+
# @example Using the throbber helper
|
407
|
+
# command :slow do
|
408
|
+
# throbber do
|
409
|
+
# # Add some long running code here
|
410
|
+
# ...
|
411
|
+
# end
|
412
|
+
# end
|
413
|
+
def throbber(&block)
|
414
|
+
hidden_output do |ostream|
|
415
|
+
code_thread = Thread.new { block.call }
|
416
|
+
throbber_thread = Throbber.new(ostream, code_thread)
|
417
|
+
|
418
|
+
code_thread.join
|
419
|
+
throbber_thread.join
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
end
|