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