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/lib/rubikon/command.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
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
5
|
|
6
6
|
require 'rubikon/application/base'
|
7
|
-
require 'rubikon/
|
7
|
+
require 'rubikon/errors'
|
8
8
|
require 'rubikon/has_arguments'
|
9
9
|
require 'rubikon/parameter'
|
10
10
|
|
@@ -34,16 +34,14 @@ module Rubikon
|
|
34
34
|
# @param [Application::Base] app The application this command belongs to
|
35
35
|
# @param [Symbol, #to_sym] name The name of this command, used in application
|
36
36
|
# arguments
|
37
|
-
# @param
|
38
|
-
# command takes.
|
37
|
+
# @param options (see HasArguments#initialize)
|
39
38
|
# @param [Proc] block The code block which should be executed by this
|
40
39
|
# command
|
41
40
|
# @raise [ArgumentError] if the given application object isn't a Rubikon
|
42
41
|
# application
|
43
42
|
# @raise [BlockMissingError] if no command code block is given and a
|
44
43
|
# command file does not exist
|
45
|
-
|
46
|
-
def initialize(app, name, arg_count = nil, &block)
|
44
|
+
def initialize(app, name, *options, &block)
|
47
45
|
super
|
48
46
|
|
49
47
|
@params = {}
|
@@ -58,8 +56,81 @@ module Rubikon
|
|
58
56
|
end
|
59
57
|
end
|
60
58
|
|
59
|
+
# Generate help for this command
|
60
|
+
#
|
61
|
+
# @param [Boolean] show_usage If +true+, the returned String will also
|
62
|
+
# include usage information
|
63
|
+
# @return [String] The contents of the help screen for this command
|
64
|
+
# @since 0.6.0
|
65
|
+
def help(show_usage = true)
|
66
|
+
help = ''
|
67
|
+
|
68
|
+
if show_usage
|
69
|
+
help << " #{name}" if name != :__default
|
70
|
+
|
71
|
+
@params.values.uniq.sort_by {|a| a.name.to_s }.each do |param|
|
72
|
+
help << ' ['
|
73
|
+
([param.name] + param.aliases).each_with_index do |name, index|
|
74
|
+
name = name.to_s
|
75
|
+
help << '|' if index > 0
|
76
|
+
help << '-' if name.size > 1
|
77
|
+
help << "-#{name}"
|
78
|
+
end
|
79
|
+
help << ' ...' if param.is_a?(Option)
|
80
|
+
help << ']'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
help << "\n\n#{description}" unless description.nil?
|
85
|
+
|
86
|
+
help_flags = {}
|
87
|
+
help_options = {}
|
88
|
+
params.each_value do |param|
|
89
|
+
if param.is_a? Flag
|
90
|
+
help_flags[param.name.to_s] = param
|
91
|
+
else
|
92
|
+
help_options[param.name.to_s] = param
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
param_name = lambda { |name| "#{name.size > 1 ? '-' : ' '}-#{name}" }
|
97
|
+
unless help_flags.empty? && help_options.empty?
|
98
|
+
max_param_length = (help_flags.keys + help_options.keys).
|
99
|
+
max_by { |a| a.size }.size + 2
|
100
|
+
end
|
101
|
+
|
102
|
+
unless help_flags.empty?
|
103
|
+
help << "\n\nFlags:"
|
104
|
+
help_flags.sort_by { |name, param| name }.each do |name, param|
|
105
|
+
help << "\n #{param_name.call(name).ljust(max_param_length)}"
|
106
|
+
help << " #{param.description}" unless param.description.nil?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
unless help_options.empty?
|
111
|
+
help << "\n\nOptions:\n"
|
112
|
+
help_options.sort_by { |name, param| name }.each do |name, param|
|
113
|
+
help << " #{param_name.call(name).ljust(max_param_length)} ..."
|
114
|
+
help << " #{param.description}" unless param.description.nil?
|
115
|
+
help << "\n"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
help
|
120
|
+
end
|
121
|
+
|
61
122
|
private
|
62
123
|
|
124
|
+
# Returns all parameters of this command that are active, i.e. that have
|
125
|
+
# been supplied on the command-line
|
126
|
+
#
|
127
|
+
# @return [Array<Parameter>] All currently active parameters of this
|
128
|
+
# command
|
129
|
+
# @since 0.6.0
|
130
|
+
def active_params
|
131
|
+
@params.values.select { |param| param.active? }
|
132
|
+
end
|
133
|
+
|
63
134
|
# Add a new parameter for this command
|
64
135
|
#
|
65
136
|
# @param [Parameter, Hash] parameter The parameter to add to this
|
@@ -96,7 +167,6 @@ module Rubikon
|
|
96
167
|
# method will return the value of the parameter.
|
97
168
|
#
|
98
169
|
# @param (see ClassMethods#method_missing)
|
99
|
-
# @see DSLMethods#params
|
100
170
|
#
|
101
171
|
# @example
|
102
172
|
# option :user, [:who]
|
@@ -105,52 +175,17 @@ module Rubikon
|
|
105
175
|
# puts "I feel #{mood}"
|
106
176
|
# end
|
107
177
|
def method_missing(name, *args, &block)
|
108
|
-
if args.empty? && !block_given?
|
109
|
-
@params
|
110
|
-
|
111
|
-
super
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# Parses the arguments of this command and sets each Parameter as active
|
116
|
-
# if it has been supplied by the user on the command-line. Additional
|
117
|
-
# arguments are passed to the individual parameters.
|
118
|
-
#
|
119
|
-
# @param [Array<String>] args The arguments that have been passed to this
|
120
|
-
# command
|
121
|
-
# @raise [UnknownParameterError] if an undefined parameter is passed to the
|
122
|
-
# command
|
123
|
-
# @see Flag
|
124
|
-
# @see Option
|
125
|
-
def parse_arguments(args)
|
126
|
-
current_param = Application::InstanceMethods.
|
127
|
-
instance_method(:current_param).bind(@app)
|
128
|
-
set_current_param = Application::InstanceMethods.
|
129
|
-
instance_method(:current_param=).bind(@app)
|
130
|
-
|
131
|
-
@args = []
|
132
|
-
args.each do |arg|
|
133
|
-
if arg.start_with?('-')
|
134
|
-
parameter_name = arg.start_with?('--') ? arg[2..-1] : arg[1..-1]
|
135
|
-
parameter = @params[parameter_name.to_sym]
|
136
|
-
raise UnknownParameterError.new(arg) if parameter.nil?
|
137
|
-
end
|
138
|
-
|
139
|
-
unless parameter.nil?
|
140
|
-
current_param.call.send(:active!) unless current_param.call.nil?
|
141
|
-
set_current_param.call(parameter)
|
142
|
-
next
|
143
|
-
end
|
144
|
-
|
145
|
-
if current_param.call.nil? || !current_param.call.send(:more_args?)
|
146
|
-
self << arg
|
178
|
+
if args.empty? && !block_given?
|
179
|
+
if @params.key?(name)
|
180
|
+
return @params[name]
|
147
181
|
else
|
148
|
-
|
182
|
+
active_params.each do |param|
|
183
|
+
return param.send(name) if param.respond_to_missing?(name)
|
184
|
+
end
|
149
185
|
end
|
150
186
|
end
|
151
187
|
|
152
|
-
|
153
|
-
set_current_param.call(nil)
|
188
|
+
super
|
154
189
|
end
|
155
190
|
|
156
191
|
# Resets this command to its initial state
|
@@ -172,15 +207,13 @@ module Rubikon
|
|
172
207
|
# @return +true+ if named parameter with the specified name exists
|
173
208
|
# @see #method_missing
|
174
209
|
def respond_to_missing?(name, include_private = false)
|
175
|
-
@params.key?(name) ||
|
210
|
+
@params.key?(name) ||
|
211
|
+
active_params.any? { |param| param.respond_to_missing?(name) } ||
|
212
|
+
super
|
176
213
|
end
|
177
214
|
|
178
215
|
# Run this command's code block
|
179
|
-
|
180
|
-
# @param [Array<String>] args The arguments that have been passed to this
|
181
|
-
# command
|
182
|
-
def run(*args)
|
183
|
-
parse_arguments(args)
|
216
|
+
def run
|
184
217
|
check_args
|
185
218
|
Application::InstanceMethods.instance_method(:sandbox).bind(@app).call.
|
186
219
|
instance_eval(&@block)
|
@@ -1,7 +1,7 @@
|
|
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
5
|
|
6
6
|
module Rubikon
|
7
7
|
|
@@ -19,14 +19,39 @@ module Rubikon
|
|
19
19
|
#
|
20
20
|
# @param [String] file The path of the config file to load
|
21
21
|
# @return [Hash] The configuration values loaded from the file
|
22
|
+
# @see IniProvider
|
22
23
|
# @see YamlProvider
|
23
24
|
def self.load_config(file)
|
25
|
+
provider_for(file).load_config(file)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Saves a configuration Hash with the corresponding provider detected
|
29
|
+
# from the file extension
|
30
|
+
#
|
31
|
+
# @param [Hash] config The configuration to write
|
32
|
+
# @param [String] file The path of the file to write
|
33
|
+
# @see IniProvider
|
34
|
+
# @see YamlProvider
|
35
|
+
# @since 0.6.0
|
36
|
+
def self.save_config(config, file)
|
37
|
+
provider_for(file).save_config(config, file)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Returns the correct provider for the given file
|
43
|
+
#
|
44
|
+
# The file format is guessed from the file extension.
|
45
|
+
#
|
46
|
+
# @return Object A provider for the given file format
|
47
|
+
# @since 0.6.0
|
48
|
+
def provider_for(file)
|
24
49
|
ext = File.extname(file)
|
25
50
|
case ext
|
26
51
|
when '.ini'
|
27
|
-
IniProvider
|
52
|
+
IniProvider
|
28
53
|
when '.yaml', '.yml'
|
29
|
-
YamlProvider
|
54
|
+
YamlProvider
|
30
55
|
else
|
31
56
|
raise UnsupportedConfigFormatError.new(ext)
|
32
57
|
end
|
@@ -1,7 +1,7 @@
|
|
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
5
|
|
6
6
|
require 'rubikon/config/auto_provider'
|
7
7
|
require 'rubikon/config/ini_provider'
|
@@ -45,19 +45,32 @@ module Rubikon
|
|
45
45
|
# configuration data from the files found
|
46
46
|
def initialize(name, search_paths, provider = :yaml)
|
47
47
|
provider = :auto unless PROVIDERS.include?(provider)
|
48
|
-
provider = Config.const_get("#{provider.to_s.capitalize}Provider")
|
48
|
+
@provider = Config.const_get("#{provider.to_s.capitalize}Provider")
|
49
49
|
|
50
50
|
@files = []
|
51
51
|
@config = {}
|
52
52
|
search_paths.each do |path|
|
53
53
|
config_file = File.join path, name
|
54
54
|
if File.exists? config_file
|
55
|
-
@config.merge! provider.load_config(config_file)
|
55
|
+
@config.merge! @provider.load_config(config_file)
|
56
56
|
@files << config_file
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
# Save the given configuration into the specified file
|
62
|
+
#
|
63
|
+
# @param [Hash] The configuration to save
|
64
|
+
# @param [String] The file path where the configuration should be saved
|
65
|
+
# @since 0.6.0
|
66
|
+
def save_config(config, file)
|
67
|
+
unless config.is_a? Hash
|
68
|
+
raise ArgumentError.new('Configuration has to be a Hash')
|
69
|
+
end
|
70
|
+
|
71
|
+
@provider.save_config config, file
|
72
|
+
end
|
73
|
+
|
61
74
|
end
|
62
75
|
|
63
76
|
end
|
@@ -1,7 +1,7 @@
|
|
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
5
|
|
6
6
|
module Rubikon
|
7
7
|
|
@@ -53,6 +53,34 @@ module Rubikon
|
|
53
53
|
config
|
54
54
|
end
|
55
55
|
|
56
|
+
# Saves a configuration Hash into a INI file
|
57
|
+
#
|
58
|
+
# @param [Hash] config The configuration to write
|
59
|
+
# @param [String] file The path of the file to write
|
60
|
+
# @since 0.6.0
|
61
|
+
def self.save_config(config, file)
|
62
|
+
unless config.is_a? Hash
|
63
|
+
raise ArgumentError.new('Configuration has to be a Hash')
|
64
|
+
end
|
65
|
+
|
66
|
+
file = File.new file, 'w'
|
67
|
+
|
68
|
+
config.each do |key, value|
|
69
|
+
if value.is_a? Hash
|
70
|
+
file << "\n" if file.pos > 0
|
71
|
+
file << "[#{key.to_s}]\n"
|
72
|
+
|
73
|
+
value.each do |k, v|
|
74
|
+
file << " #{k.to_s} = #{v.to_s unless v.nil?}\n"
|
75
|
+
end
|
76
|
+
else
|
77
|
+
file << "#{key.to_s} = #{value.to_s unless value.nil?}\n"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
file.close
|
82
|
+
end
|
83
|
+
|
56
84
|
end
|
57
85
|
|
58
86
|
end
|
@@ -1,7 +1,7 @@
|
|
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
5
|
|
6
6
|
require 'yaml'
|
7
7
|
|
@@ -23,6 +23,21 @@ module Rubikon
|
|
23
23
|
YAML.load_file file
|
24
24
|
end
|
25
25
|
|
26
|
+
# Saves a configuration Hash into a YAML formatted file
|
27
|
+
#
|
28
|
+
# @param [Hash] config The configuration to write
|
29
|
+
# @param [String] file The path of the file to write
|
30
|
+
# @since 0.6.0
|
31
|
+
def self.save_config(config, file)
|
32
|
+
unless config.is_a? Hash
|
33
|
+
raise ArgumentError.new('Configuration has to be a Hash')
|
34
|
+
end
|
35
|
+
|
36
|
+
file = File.new file, 'w'
|
37
|
+
YAML.dump config, file
|
38
|
+
file.close
|
39
|
+
end
|
40
|
+
|
26
41
|
end
|
27
42
|
|
28
43
|
end
|
@@ -1,7 +1,7 @@
|
|
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
|
module Rubikon
|
7
7
|
|
@@ -62,8 +62,32 @@ module Rubikon
|
|
62
62
|
# @since 0.3.0
|
63
63
|
class UnknownCommandError < ArgumentError
|
64
64
|
|
65
|
+
# @return [Symbol] The name of the command that has been tried to access
|
66
|
+
attr_reader :command
|
67
|
+
|
68
|
+
# Creates a new error and stores the name of the command that could not be
|
69
|
+
# found
|
70
|
+
#
|
71
|
+
# @param [Symbol] name The name of the unknown command
|
65
72
|
def initialize(name)
|
66
73
|
super "Unknown command: #{name}"
|
74
|
+
@command = name
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
# Raised if an argument is passed, that does not match a validation rule
|
80
|
+
#
|
81
|
+
# @author Sebastian Staudt
|
82
|
+
# @see HasArguments#check_args
|
83
|
+
# @since 0.6.0
|
84
|
+
class UnexpectedArgumentError < ArgumentError
|
85
|
+
|
86
|
+
# Creates a new error and stores the given argument value
|
87
|
+
#
|
88
|
+
# @param [Symbol] arg The given argument value
|
89
|
+
def initialize(arg)
|
90
|
+
super "Unexpected argument: #{arg}"
|
67
91
|
end
|
68
92
|
|
69
93
|
end
|
@@ -1,7 +1,7 @@
|
|
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
5
|
|
6
6
|
require 'rubikon/parameter'
|
7
7
|
|
@@ -19,47 +19,114 @@ module Rubikon
|
|
19
19
|
|
20
20
|
include Parameter
|
21
21
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
22
|
+
# Provides a number of predefined regular expressions to check arguments
|
23
|
+
# against
|
24
|
+
#
|
25
|
+
# @see #initialize
|
26
|
+
# @since 0.6.0
|
27
|
+
ARGUMENT_MATCHERS = {
|
28
|
+
# Allow only alphanumeric characters
|
29
|
+
:alnum => /[[:alnum:]]+/,
|
30
|
+
# Allow only floating point numbers as arguments
|
31
|
+
:float => /-?[0-9]+(?:\.[0-9]+)?/,
|
32
|
+
# Allow only alphabetic characters
|
33
|
+
:letters => /[a-zA-Z]+/,
|
34
|
+
# Allow only numeric arguments
|
35
|
+
:numeric => /-?[0-9]+/
|
36
|
+
}
|
25
37
|
|
26
38
|
# Creates a new parameter with arguments with the given name and an
|
27
39
|
# optional code block
|
28
40
|
#
|
29
41
|
# @param [Application::Base] app The application this parameter belongs to
|
30
|
-
# @param [Symbol, #to_sym] name The name of the
|
31
|
-
# @param [
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# of required arguments
|
36
|
-
# the amount of required arguments, but allows additional, optional
|
42
|
+
# @param [Symbol, #to_sym] name The name of the parameter
|
43
|
+
# @param [Array] options A range allows any number of arguments inside the
|
44
|
+
# limits of the range or array (-1 stands for an arbitrary number of
|
45
|
+
# arguments). A positive number indicates the exact amount of
|
46
|
+
# required arguments while a negative argument count indicates the
|
47
|
+
# amount of required arguments, but allows additional, optional
|
37
48
|
# arguments. A argument count of 0 means there are no required
|
38
|
-
# arguments, but it allows optional arguments.
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
49
|
+
# arguments, but it allows optional arguments. An array of
|
50
|
+
# symbols enables named arguments where the argument count is the
|
51
|
+
# size of the array and each argument is named after the
|
52
|
+
# corresponding symbol. Finally a hash may be used to specify
|
53
|
+
# options for named arguments. The keys of the hash will be the
|
54
|
+
# names of the arguments and the values are options for this
|
55
|
+
# argument. You may specify multiple options as an array. Possible
|
56
|
+
# options are:
|
57
|
+
# - +:optional+ makes the argument optional
|
58
|
+
# - +:remainder+ makes the argument take all remaining arguments as
|
59
|
+
# an array
|
60
|
+
# - One or more strings will cause the argument to be checked to be
|
61
|
+
# equal to one of the strings
|
62
|
+
# - One or more regular expressions will cause the argument to be
|
63
|
+
# checked to match one of the expressions
|
64
|
+
# - Other symbols may reference to a predefined regular expression
|
65
|
+
# from {ARGUMENT_MATCHERS}
|
42
66
|
# @param [Proc] block An optional code block to be executed if this
|
43
67
|
# option is used
|
44
|
-
def initialize(app, name,
|
68
|
+
def initialize(app, name, *options, &block)
|
45
69
|
super(app, name, &block)
|
46
70
|
|
47
|
-
@
|
48
|
-
@
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
71
|
+
@arg_names = []
|
72
|
+
@arg_values = {}
|
73
|
+
@args = {}
|
74
|
+
|
75
|
+
@description = options.shift if options.first.is_a? String
|
76
|
+
|
77
|
+
if options.size == 1 && (options.first.nil? ||
|
78
|
+
options.first.is_a?(Fixnum) || options.first.is_a?(Range))
|
79
|
+
options = options.first
|
80
|
+
end
|
81
|
+
|
82
|
+
if options.is_a? Fixnum
|
83
|
+
if options > 0
|
84
|
+
@min_arg_count = options
|
85
|
+
@max_arg_count = options
|
86
|
+
elsif options <= 0
|
87
|
+
@min_arg_count = -options
|
55
88
|
@max_arg_count = -1
|
56
89
|
end
|
57
|
-
elsif
|
58
|
-
@
|
59
|
-
@
|
60
|
-
elsif
|
61
|
-
@
|
62
|
-
@max_arg_count =
|
90
|
+
elsif options.is_a? Range
|
91
|
+
@min_arg_count = options.first
|
92
|
+
@max_arg_count = options.last
|
93
|
+
elsif options.is_a? Array
|
94
|
+
@arg_names = []
|
95
|
+
@max_arg_count = 0
|
96
|
+
@min_arg_count = 0
|
97
|
+
options.each do |arg|
|
98
|
+
if arg.is_a? Hash
|
99
|
+
arg = arg.map do |arg_name, opt|
|
100
|
+
[arg_name, opt.is_a?(Array) ? opt : [opt]]
|
101
|
+
end
|
102
|
+
arg = arg.sort_by do |arg_name, opt|
|
103
|
+
opt.include?(:optional) ? 1 : 0
|
104
|
+
end
|
105
|
+
arg.each do |arg_name, opt|
|
106
|
+
matchers = opt.reject { |o| [:optional, :remainder].include? o }
|
107
|
+
opt -= matchers
|
108
|
+
@arg_names << arg_name.to_sym
|
109
|
+
if !matchers.empty?
|
110
|
+
matchers.map! do |m|
|
111
|
+
ARGUMENT_MATCHERS[m] || (m.is_a?(Regexp) ? m : m.to_s)
|
112
|
+
end
|
113
|
+
@arg_values[arg_name] = /^#{Regexp.union *matchers}$/
|
114
|
+
end
|
115
|
+
unless opt.include? :optional
|
116
|
+
@min_arg_count += 1
|
117
|
+
end
|
118
|
+
if opt.include? :remainder
|
119
|
+
@max_arg_count = -1
|
120
|
+
break
|
121
|
+
end
|
122
|
+
@max_arg_count += 1
|
123
|
+
end
|
124
|
+
else
|
125
|
+
@arg_names << arg.to_sym
|
126
|
+
@min_arg_count += 1
|
127
|
+
@max_arg_count += 1
|
128
|
+
end
|
129
|
+
end
|
63
130
|
else
|
64
131
|
@min_arg_count = 0
|
65
132
|
@max_arg_count = 0
|
@@ -68,17 +135,28 @@ module Rubikon
|
|
68
135
|
|
69
136
|
# Access the arguments of this parameter using a numeric or symbolic index
|
70
137
|
#
|
71
|
-
# @param [Numeric, Symbol] The index of the argument to return.
|
72
|
-
# indices can be used always while symbolic arguments are
|
73
|
-
# available for named arguments.
|
138
|
+
# @param [Numeric, Symbol] arg The name or index of the argument to return.
|
139
|
+
# Numeric indices can be used always while symbolic arguments are
|
140
|
+
# only available for named arguments.
|
74
141
|
# @return The argument with the specified index
|
75
142
|
# @see #args
|
76
143
|
# @since 0.4.0
|
77
144
|
def [](arg)
|
78
|
-
arg = @arg_names.index(arg) if arg.is_a? Symbol
|
79
145
|
@args[arg]
|
80
146
|
end
|
81
147
|
|
148
|
+
# Returns the arguments given to this parameter. They are given as a Hash
|
149
|
+
# when there are named arguments or as an Array when there are no named
|
150
|
+
# arguments
|
151
|
+
#
|
152
|
+
# @return [Array<String>, Hash<Symbol, String>] The arguments given to this
|
153
|
+
# parameter
|
154
|
+
# @since 0.6.0
|
155
|
+
def args
|
156
|
+
@arg_names.empty? ? @args.values : @args
|
157
|
+
end
|
158
|
+
alias_method :arguments, :args
|
159
|
+
|
82
160
|
protected
|
83
161
|
|
84
162
|
# Adds an argument to this parameter. Arguments can be accessed inside the
|
@@ -93,10 +171,20 @@ module Rubikon
|
|
93
171
|
# @see #args
|
94
172
|
# @since 0.3.0
|
95
173
|
def <<(arg)
|
96
|
-
|
97
|
-
|
174
|
+
raise ExtraArgumentError.new(@name) unless more_args?
|
175
|
+
|
176
|
+
if @arg_names.size > @args.size
|
177
|
+
name = @arg_names[@args.size]
|
178
|
+
if @max_arg_count == -1 && @arg_names.size == @args.size + 1
|
179
|
+
@args[name] = [arg]
|
180
|
+
else
|
181
|
+
@args[name] = arg
|
182
|
+
end
|
183
|
+
elsif !@arg_names.empty? && @max_arg_count == -1
|
184
|
+
@args[@arg_names.last] << arg
|
185
|
+
else
|
186
|
+
@args[@args.size] = arg
|
98
187
|
end
|
99
|
-
@args << arg
|
100
188
|
end
|
101
189
|
|
102
190
|
# Marks this parameter as active when it has been supplied by the user on
|
@@ -131,6 +219,18 @@ module Rubikon
|
|
131
219
|
# @since 0.3.0
|
132
220
|
def check_args
|
133
221
|
raise MissingArgumentError.new(@name) unless args_full?
|
222
|
+
unless @arg_values.empty?
|
223
|
+
@args.each do |name, arg|
|
224
|
+
if @arg_values.key? name
|
225
|
+
arg = [arg] unless arg.is_a? Array
|
226
|
+
arg.each do |a|
|
227
|
+
unless a =~ @arg_values[name]
|
228
|
+
raise UnexpectedArgumentError.new(a)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
134
234
|
end
|
135
235
|
|
136
236
|
# If a named argument with the specified method name exists, a call to that
|
@@ -145,8 +245,8 @@ module Rubikon
|
|
145
245
|
# @user = name
|
146
246
|
# end
|
147
247
|
def method_missing(name, *args, &block)
|
148
|
-
if args.empty? && !block_given? &&
|
149
|
-
@args[
|
248
|
+
if args.empty? && !block_given? && @arg_names.include?(name)
|
249
|
+
@args[name]
|
150
250
|
else
|
151
251
|
super
|
152
252
|
end
|
@@ -177,7 +277,7 @@ module Rubikon
|
|
177
277
|
# @return +true+ if named argument with the specified name exists
|
178
278
|
# @see #method_missing
|
179
279
|
def respond_to_missing?(name, include_private = false)
|
180
|
-
|
280
|
+
@arg_names.include? name
|
181
281
|
end
|
182
282
|
|
183
283
|
end
|