rubycom 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +162 -146
- data/Rakefile +12 -12
- data/lib/rubycom.rb +156 -226
- data/lib/rubycom/arg_parse.rb +252 -0
- data/lib/rubycom/command_interface.rb +97 -0
- data/lib/rubycom/completions.rb +62 -0
- data/lib/rubycom/error_handler.rb +15 -0
- data/lib/rubycom/executor.rb +23 -0
- data/lib/rubycom/helpers.rb +98 -0
- data/lib/rubycom/output_handler.rb +15 -0
- data/lib/rubycom/parameter_extract.rb +262 -0
- data/lib/rubycom/singleton_commands.rb +78 -0
- data/lib/rubycom/sources.rb +99 -0
- data/lib/rubycom/version.rb +1 -1
- data/lib/rubycom/yard_doc.rb +146 -0
- data/rubycom.gemspec +14 -16
- data/test/rubycom/arg_parse_test.rb +247 -0
- data/test/rubycom/command_interface_test.rb +293 -0
- data/test/rubycom/completions_test.rb +94 -0
- data/test/rubycom/error_handler_test.rb +72 -0
- data/test/rubycom/executor_test.rb +64 -0
- data/test/rubycom/helpers_test.rb +467 -0
- data/test/rubycom/output_handler_test.rb +76 -0
- data/test/rubycom/parameter_extract_test.rb +141 -0
- data/test/rubycom/rubycom_test.rb +290 -548
- data/test/rubycom/singleton_commands_test.rb +122 -0
- data/test/rubycom/sources_test.rb +59 -0
- data/test/rubycom/util_test_bin.rb +8 -0
- data/test/rubycom/util_test_composite.rb +23 -20
- data/test/rubycom/util_test_module.rb +142 -112
- data/test/rubycom/util_test_no_singleton.rb +2 -2
- data/test/rubycom/util_test_sub_module.rb +13 -0
- data/test/rubycom/yard_doc_test.rb +165 -0
- metadata +61 -24
- data/lib/rubycom/arguments.rb +0 -133
- data/lib/rubycom/commands.rb +0 -63
- data/lib/rubycom/documentation.rb +0 -212
- data/test/rubycom/arguments_test.rb +0 -289
- data/test/rubycom/commands_test.rb +0 -51
- data/test/rubycom/documentation_test.rb +0 -186
- data/test/rubycom/util_test_job.yaml +0 -21
- data/test/rubycom/utility_tester.rb +0 -17
@@ -0,0 +1,252 @@
|
|
1
|
+
module Rubycom
|
2
|
+
module ArgParse
|
3
|
+
require 'parslet'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
# Runs a parser against the given Array of arguments to match command argument, option, and flag patterns.
|
7
|
+
#
|
8
|
+
# @param [Array] command_line an array of strings representing the arguments taken from the command line
|
9
|
+
# @return [Hash] :args => Array of arguments, :opts => Hash mapping each unique option/flag to their values
|
10
|
+
def self.parse_command_line(command_line)
|
11
|
+
raise ArgumentError, "command_line should be String or Array but was #{command_line.class}" unless [String, Array].include?(command_line.class)
|
12
|
+
command_line = command_line.dup
|
13
|
+
command_line = [command_line] if command_line.class == String
|
14
|
+
command_line = self.combine_options(command_line)
|
15
|
+
begin
|
16
|
+
command_line.map { |word|
|
17
|
+
ArgTransform.new.apply(ArgParser.new.parse(word))
|
18
|
+
}.reduce({}) { |acc, n|
|
19
|
+
# the handlers for opt and flag accumulate all unique mentions of an option name
|
20
|
+
if n.has_key?(:opt)
|
21
|
+
acc[:opts] = {} unless acc.has_key?(:opts)
|
22
|
+
acc[:opts] = acc[:opts].update(n[:opt]) { |key, old, new|
|
23
|
+
if old.class == Array
|
24
|
+
acc[:opts][key] = old << new
|
25
|
+
else
|
26
|
+
acc[:opts][key] = [old] << new
|
27
|
+
end
|
28
|
+
}
|
29
|
+
elsif n.has_key?(:flag)
|
30
|
+
acc[:opts] = {} unless acc.has_key?(:opts)
|
31
|
+
acc[:opts] = acc[:opts].update(n[:flag]) { |key, old, new|
|
32
|
+
if old.class == Array
|
33
|
+
combined = old
|
34
|
+
else
|
35
|
+
combined = [old]
|
36
|
+
end
|
37
|
+
|
38
|
+
if new.class == Array
|
39
|
+
new.each { |new_flag|
|
40
|
+
combined << new_flag
|
41
|
+
}
|
42
|
+
else
|
43
|
+
combined << new
|
44
|
+
end
|
45
|
+
|
46
|
+
acc[:opts][key] = combined
|
47
|
+
}
|
48
|
+
else
|
49
|
+
acc[:args] = [] unless acc.has_key?(:args)
|
50
|
+
acc[:args] << n[:arg]
|
51
|
+
end
|
52
|
+
acc
|
53
|
+
}
|
54
|
+
rescue Parslet::ParseFailed => failure
|
55
|
+
raise ArgParseError, "Arguments could not be parsed.", failure
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Matches a word representing an optional key to the separator and/or value which goes with the key
|
60
|
+
#
|
61
|
+
# @param [Array] command_line an array of strings representing the arguments taken from the command line
|
62
|
+
# @return [Array] an Array of Strings with matched items combined into one entry per matched set
|
63
|
+
def self.combine_options(command_line)
|
64
|
+
command_line.reduce([]) { |acc, next_word|
|
65
|
+
if next_word == '=' || (!next_word.start_with?('-') && acc.last.to_s.start_with?('-') && acc.last.to_s.end_with?('='))
|
66
|
+
acc[-1] = acc[-1].dup << next_word
|
67
|
+
acc
|
68
|
+
elsif next_word == '=' || (!next_word.start_with?('-') && acc.last.to_s.start_with?('-') && !(acc.last.to_s.include?('=') || acc.last.to_s.include?(' ')))
|
69
|
+
acc[-1] = acc[-1].dup << ' ' << next_word
|
70
|
+
acc
|
71
|
+
else
|
72
|
+
acc << next_word
|
73
|
+
end
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Comprised of grammar rules which determine whether a given string is an argument, option, or flag.
|
78
|
+
# Calling #parse with a String on an instance of this class will return a nested hash structure which identifies the
|
79
|
+
# patterns recognized in the given string.
|
80
|
+
# In order to use this parser on a command line argument array, it may be necessary to pre-join optional argument
|
81
|
+
# keys to their values such that each item in the array is either a complete argument, complete optional-argument,
|
82
|
+
# or a flag.
|
83
|
+
#
|
84
|
+
# Example:
|
85
|
+
# Rubycom::ArgParse::ArgParser.new.parse("-test_arg = test")
|
86
|
+
# => {:opt=>{:key=>"-test_arg"@0, :sep=>" = "@9, :val=>"test"@12}}
|
87
|
+
class ArgParser < Parslet::Parser
|
88
|
+
rule(:space) { match('\s').repeat(1) }
|
89
|
+
rule(:eq) { match('=') }
|
90
|
+
rule(:separator) { (eq | (space >> eq >> space) | (space >> eq) | (eq >> space) | space) }
|
91
|
+
rule(:escape_char) { match(/\\/) }
|
92
|
+
rule(:escaped_char) { escape_char >> any }
|
93
|
+
rule(:d_quote) { escape_char.absent? >> match(/"/) }
|
94
|
+
rule(:s_quote) { escape_char.absent? >> match(/'/) }
|
95
|
+
|
96
|
+
rule(:double_escaped) { d_quote >> (escape_char.absent? >> match(/[^"]/)).repeat >> d_quote }
|
97
|
+
rule(:single_escaped) { s_quote >> (escape_char.absent? >> match(/[^']/)).repeat >> s_quote }
|
98
|
+
rule(:escaped_word) { single_escaped | double_escaped }
|
99
|
+
rule(:raw_word) { (escaped_char | (separator.absent? >> any)).repeat(1) }
|
100
|
+
rule(:word) { raw_word | single_escaped | double_escaped }
|
101
|
+
rule(:list) { word >> (match(',') >> word).repeat(1) }
|
102
|
+
|
103
|
+
rule(:short) { match('-') }
|
104
|
+
rule(:long) { short >> short }
|
105
|
+
rule(:neg_opt_prefix) { (long | short) >> str('-').absent? >> (str('no-') | str('NO-')) >> word }
|
106
|
+
rule(:opt_prefix) { (long | short) >> str('-').absent? >> word }
|
107
|
+
|
108
|
+
rule(:arg) { any.repeat }
|
109
|
+
rule(:flag) { (neg_opt_prefix | opt_prefix) }
|
110
|
+
rule(:opt) { opt_prefix.as(:key) >> separator.as(:sep) >> any.repeat.as(:val) }
|
111
|
+
|
112
|
+
rule(:expression) { opt.as(:opt) | flag.as(:flag) | arg.as(:arg) }
|
113
|
+
|
114
|
+
root :expression
|
115
|
+
end
|
116
|
+
|
117
|
+
# Parslet transformer intended for use with ArgParser. Uses functions in Rubycom::ArgParse to clean up a structure
|
118
|
+
# identified by the parser and convert values to basic types.
|
119
|
+
#
|
120
|
+
# Example:
|
121
|
+
# ArgTransform.new.apply(ArgParser.new.parse("-test_arg = test"))
|
122
|
+
# => {:opt=>{"test_arg"=>"test"}}
|
123
|
+
class ArgTransform < Parslet::Transform
|
124
|
+
rule(:arg => simple(:arg)) { Rubycom::ArgParse.transform(:arg, arg) }
|
125
|
+
rule(:opt => subtree(:opt)) { Rubycom::ArgParse.transform(:opt, opt) }
|
126
|
+
rule(:flag => simple(:flag)) { Rubycom::ArgParse.transform(:flag, flag) }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Calls one of the transform functions according to the matched_type
|
130
|
+
#
|
131
|
+
# @param [Symbol] matched_type :arg, :opt, :flag will transform the value according to the corresponding transform function.
|
132
|
+
# anything else will extract the value as a string
|
133
|
+
# @param [Hash|Parslet::Slice] val a possibly nested Hash structure or a Slice. Hashes are returned by the parser when
|
134
|
+
# it matches a complex pattern, a Slice will be returned when the matched pattern is not tree like.
|
135
|
+
# @return [Hash] :arg => value | :opt|:flag => a Hash mapping keys to values
|
136
|
+
def self.transform(matched_type, val, loaders={}, transformers={})
|
137
|
+
loader_methods = {
|
138
|
+
arg: Rubycom::ArgParse.public_method(:load_string),
|
139
|
+
opt: Rubycom::ArgParse.public_method(:load_opt_value),
|
140
|
+
flag: Rubycom::ArgParse.public_method(:load_flag_value)
|
141
|
+
}.merge(loaders)
|
142
|
+
transforms = {
|
143
|
+
arg: Rubycom::ArgParse.public_method(:transform_arg),
|
144
|
+
opt: Rubycom::ArgParse.public_method(:transform_opt),
|
145
|
+
flag: Rubycom::ArgParse.public_method(:transform_flag)
|
146
|
+
}.merge(transformers)
|
147
|
+
|
148
|
+
{
|
149
|
+
matched_type => if [:arg,:opt,:flag].include?(matched_type)
|
150
|
+
transforms[matched_type].call(val, loader_methods[matched_type])
|
151
|
+
else
|
152
|
+
val.str.strip
|
153
|
+
end
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
# Uses the given arg_loader to resolve the ruby type for the given string
|
158
|
+
#
|
159
|
+
# @param [String|Parslet::Slice] match_string a string identified as an argument
|
160
|
+
# @param [Method|Proc] arg_loader called to load the value(s)
|
161
|
+
# @return [Object] the result of a call to the given arg_loader
|
162
|
+
def self.transform_arg(match_string, arg_loader=Rubycom::ArgParse.public_method(:load_string))
|
163
|
+
match_string = match_string.str.strip if match_string.class == Parslet::Slice
|
164
|
+
arg_loader.call(match_string)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Uses the given opt_loader to resolve the ruby type for the value in the given Hash
|
168
|
+
#
|
169
|
+
# @param [Hash] subtree a structure identified as an option, must have keys :key, :sep, :val
|
170
|
+
# @param [Method|Proc] opt_loader called to load the option value(s)
|
171
|
+
# @return [Hash] mapping the option key to it's loaded value
|
172
|
+
def self.transform_opt(subtree, opt_loader=Rubycom::ArgParse.public_method(:load_opt_value))
|
173
|
+
val = subtree[:val].str
|
174
|
+
val = val.split(',') unless (val.start_with?('[') && val.end_with?(']'))
|
175
|
+
value = opt_loader.call(val)
|
176
|
+
{
|
177
|
+
subtree[:key].str.reverse.chomp('-').chomp('-').reverse => value
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
# Calls the given loader to load a single string or array of strings
|
182
|
+
#
|
183
|
+
# @param [Array] value containing the string(s) to be loaded
|
184
|
+
# @param [Method|Proc] loader called to load the value(s)
|
185
|
+
# @return [Object] the result of a call to #load_string
|
186
|
+
def self.load_opt_value(value, loader=Rubycom::ArgParse.public_method(:load_string))
|
187
|
+
if value.class == Array
|
188
|
+
(value.length == 1) ? loader.call(value.first) : value.map { |v| loader.call(v) }
|
189
|
+
else
|
190
|
+
loader.call(value)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Uses the given loader to resolve the ruby type for the given string
|
195
|
+
#
|
196
|
+
# @param [String] string to be loaded
|
197
|
+
# @param [Method|Proc] loader called to load the string
|
198
|
+
# @return [Object] the result of a call to the loader or the given string if it could not be parsed
|
199
|
+
def self.load_string(string, loader=YAML.public_method(:load))
|
200
|
+
if string.start_with?('#') || string.start_with?('!')
|
201
|
+
result = string
|
202
|
+
else
|
203
|
+
begin
|
204
|
+
result = loader.call(string)
|
205
|
+
rescue Exception
|
206
|
+
result = string
|
207
|
+
end
|
208
|
+
end
|
209
|
+
result
|
210
|
+
end
|
211
|
+
|
212
|
+
# Resolves the type and values for the given flag string
|
213
|
+
#
|
214
|
+
# @param [String|Parslet::Slice] match_string a string identified as a flag, should start with a - or -- and contain no spaces
|
215
|
+
# @return [Hash] flag_key(s) => true|false | an array of true|false if there were multiple mentions of the same short flag key
|
216
|
+
def self.transform_flag(match_string, loader=Rubycom::ArgParse.public_method(:load_flag_value))
|
217
|
+
match_string = match_string.str.strip if match_string.class == Parslet::Slice
|
218
|
+
if match_string.start_with?('--')
|
219
|
+
long_flag = match_string.reverse.chomp('-').chomp('-').reverse
|
220
|
+
long_flag_key = long_flag.sub(/no-|NO-/, '')
|
221
|
+
{
|
222
|
+
long_flag_key => loader.call(long_flag)
|
223
|
+
}
|
224
|
+
else
|
225
|
+
short_flag = match_string.reverse.chomp('-').reverse
|
226
|
+
short_flag_key = short_flag.sub(/no-|NO-/, '')
|
227
|
+
short_flag_key.split(//).map { |k|
|
228
|
+
{
|
229
|
+
k => loader.call(short_flag)
|
230
|
+
}
|
231
|
+
}.reduce({}) { |acc, n|
|
232
|
+
acc.update(n) { |_, old, new|
|
233
|
+
if old.class == Array
|
234
|
+
old << new
|
235
|
+
else
|
236
|
+
[old] << new
|
237
|
+
end
|
238
|
+
}
|
239
|
+
}
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Resolves the given flag to true or false as appropriate.
|
244
|
+
#
|
245
|
+
# @param [String] flag string representing a flag without the proceeding dashes
|
246
|
+
# @return [Boolean] FalseClass if the flag starts with a no- TrueClass otherwise
|
247
|
+
def self.load_flag_value(flag)
|
248
|
+
(flag.start_with?('no-') || flag.start_with?('NO-')) ? false : true
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Rubycom
|
2
|
+
module CommandInterface
|
3
|
+
require "#{File.dirname(__FILE__)}/helpers.rb"
|
4
|
+
|
5
|
+
# Uses #build_usage and #build_details to create a structured text output from the given command and doc hash
|
6
|
+
#
|
7
|
+
# @param [Module|Method|String] command the command to be named in the output
|
8
|
+
# @param [Hash] command_doc keys should include :full_doc and any keys required by #build_usage and #build_details
|
9
|
+
# @return [String] a structured string suitable for printing to the console as a command usage document
|
10
|
+
def self.build_interface(command, command_doc)
|
11
|
+
raise ArgumentError, "command should not be nil" if command.nil?
|
12
|
+
raise ArgumentError, "command_doc should not be nil" if command_doc.nil?
|
13
|
+
"#{self.build_usage(command, command_doc)}\n"+
|
14
|
+
"Description:\n"+
|
15
|
+
"#{command_doc.fetch(:full_doc, '').split("\n").map{|line| " #{line}"}.join("\n").chomp}\n"+
|
16
|
+
"#{self.build_details(command, command_doc)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Uses #build_options to create a usage banner for use in a command usage document
|
20
|
+
#
|
21
|
+
# @param [Module|Method|String] command the command to be named in the output
|
22
|
+
# @param [Hash] command_doc keys should include any keys required by #build_options
|
23
|
+
# @return [String] a structured text representation of usage patterns for the given command and doc hash
|
24
|
+
def self.build_usage(command, command_doc)
|
25
|
+
return '' if command.nil?
|
26
|
+
command_use = if File.basename($0, File.extname($0)).gsub("_", '') == command.name.to_s.downcase ||
|
27
|
+
File.read($0).match(/(class|module)\s+#{command.name}/)
|
28
|
+
File.basename($0)
|
29
|
+
else
|
30
|
+
command.name.to_s
|
31
|
+
end
|
32
|
+
"Usage: #{command_use} #{self.build_options(command, command_doc)}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates a structured text representation of usage patterns for the given command and doc hash
|
36
|
+
#
|
37
|
+
# @param [Module|Method|String] command the class will be used to determine the overall usage pattern
|
38
|
+
# @param [Hash] command_doc keys should include :parameters and each parameter should be a hash including keys :type, :param_name, :default
|
39
|
+
# @return [String] a structured text representation of usage patterns for the given command and doc hash
|
40
|
+
def self.build_options(command, command_doc)
|
41
|
+
return '' if command_doc.nil?
|
42
|
+
if command.class == Module
|
43
|
+
"<command> [args]"
|
44
|
+
elsif command.class == Method
|
45
|
+
args, opts = command_doc.fetch(:parameters).map { |param|
|
46
|
+
if param.fetch(:type) == :req
|
47
|
+
"<#{param[:param_name]}>"
|
48
|
+
else
|
49
|
+
"[--#{param[:param_name]} #{param[:default]}]"
|
50
|
+
end
|
51
|
+
}.group_by { |p| p.start_with?('<') }.map { |_, group| group.join(' ') }
|
52
|
+
"#{args} #{opts}"
|
53
|
+
else
|
54
|
+
""
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a structured list of either sub commands or parameters based on the class of command
|
59
|
+
# Calls #build_tags if command is a Method
|
60
|
+
#
|
61
|
+
# @param [Module|Method|String] command the class will be used to determine the usage structure
|
62
|
+
# @param [Hash] command_doc keys should include :parameters and each parameter should be a hash including keys :type, :param_name, :default
|
63
|
+
# @return [String] a structured text representing a list of sub commands or a list of parameters
|
64
|
+
def self.build_details(command, command_doc)
|
65
|
+
if command.class == Module
|
66
|
+
sub_commands = Rubycom::Helpers.format_command_list(command_doc[:sub_command_docs], 90, ' ').join()
|
67
|
+
(sub_commands.empty?)? '' : "Sub Commands:\n#{sub_commands}"
|
68
|
+
elsif command.class == Method
|
69
|
+
tags = self.build_tags(Rubycom::Helpers.format_tags(command_doc[:tags]))
|
70
|
+
"#{tags[:others]}#{tags[:params]}#{tags[:returns]}"
|
71
|
+
else
|
72
|
+
""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Creates a structured text representing the given documentation tags
|
77
|
+
#
|
78
|
+
# @param [Hash] tags :params|:returns|:others => [Strings]
|
79
|
+
# @return [Hash] the given key => String representing the list of tags
|
80
|
+
def self.build_tags(tags)
|
81
|
+
return '' if tags.nil? || tags.empty?
|
82
|
+
tags.map{|k,val_arr|
|
83
|
+
val = val_arr.map { |line| " #{line}" }.join.chomp
|
84
|
+
{
|
85
|
+
k => if k == :params
|
86
|
+
(val.empty?)? '' : "\nParameters:\n#{val}"
|
87
|
+
elsif k == :returns
|
88
|
+
(val.empty?)? '' : "\nReturns:\n#{val}"
|
89
|
+
else
|
90
|
+
(val.empty?)? '' : "\nTags:\n#{val}"
|
91
|
+
end
|
92
|
+
}
|
93
|
+
}.reduce({}, &:merge)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rubycom
|
2
|
+
module Completions
|
3
|
+
|
4
|
+
# Discovers a list of possible matches to the given arguments
|
5
|
+
# Intended for use with bash tab completion
|
6
|
+
#
|
7
|
+
# @param [Module] base the module which invoked 'include Rubycom'
|
8
|
+
# @param [Array] arguments a String Array representing the arguments to be matched
|
9
|
+
# @param [Module] command_plugin the plugin to use for retrieving commands
|
10
|
+
# @return [Array] a String Array including the possible matches for the given arguments
|
11
|
+
def self.tab_complete(base, arguments, command_plugin)
|
12
|
+
return [] unless base.class == Module
|
13
|
+
return [] unless command_plugin.class == Module
|
14
|
+
arguments = [] if arguments.nil?
|
15
|
+
args = (arguments.include?('tab_complete')) ? arguments[2..-1] : arguments
|
16
|
+
matches = %w()
|
17
|
+
if args.nil? || args.empty?
|
18
|
+
matches = command_plugin.get_commands(base, false)[base.to_s.to_sym].map { |sym,_| sym.to_s }
|
19
|
+
elsif args.length == 1
|
20
|
+
matches = command_plugin.get_commands(base, false)[base.to_s.to_sym].map { |sym,_| sym.to_s }.select { |word| !word.match(/^#{args[0]}/).nil? }
|
21
|
+
if matches.size == 1 && matches[0] == args[0]
|
22
|
+
matches = self.tab_complete(Kernel.const_get(args[0].to_sym), args[1..-1], command_plugin)
|
23
|
+
end
|
24
|
+
elsif args.length > 1
|
25
|
+
begin
|
26
|
+
matches = self.tab_complete(Kernel.const_get(args[0].to_sym), args[1..-1], command_plugin)
|
27
|
+
rescue Exception
|
28
|
+
matches = %w()
|
29
|
+
end
|
30
|
+
end unless base.nil?
|
31
|
+
matches = %w() if matches.nil? || matches.include?(args[0])
|
32
|
+
matches
|
33
|
+
end
|
34
|
+
|
35
|
+
# Inserts a tab completion into the current user's .bash_profile with a command entry to register the function for
|
36
|
+
# the current running ruby file
|
37
|
+
#
|
38
|
+
# @param [Module] base the module which invoked 'include Rubycom'
|
39
|
+
# @return [String] a message indicating the result of the command
|
40
|
+
def self.register_completions(base)
|
41
|
+
completion_function = <<-END.gsub(/^ {6}/, '')
|
42
|
+
|
43
|
+
_#{base}_complete() {
|
44
|
+
COMPREPLY=()
|
45
|
+
local completions="$(ruby #{File.absolute_path($0)} tab_complete ${COMP_WORDS[*]} 2>/dev/null)"
|
46
|
+
COMPREPLY=( $(compgen -W "$completions") )
|
47
|
+
}
|
48
|
+
complete -o bashdefault -o default -o nospace -F _#{base}_complete #{$0.split('/').last}
|
49
|
+
END
|
50
|
+
|
51
|
+
already_registered = File.readlines("#{Dir.home}/.bash_profile").map { |line| line.include?("_#{base}_complete()") }.reduce(:|) rescue false
|
52
|
+
if already_registered
|
53
|
+
"Completion function for #{base} already registered."
|
54
|
+
else
|
55
|
+
File.open("#{Dir.home}/.bash_profile", 'a+') { |file|
|
56
|
+
file.write(completion_function)
|
57
|
+
}
|
58
|
+
"Registration complete, run 'source #{Dir.home}/.bash_profile' to enable auto-completion."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Rubycom
|
2
|
+
module ErrorHandler
|
3
|
+
|
4
|
+
# Prints the error followed by the command line interface text
|
5
|
+
#
|
6
|
+
# @param [Error] e the error to be printed
|
7
|
+
# @param [String] cli_output the command line interface text to be printed
|
8
|
+
def self.handle_error(e, cli_output)
|
9
|
+
$stderr.puts e
|
10
|
+
$stderr.puts
|
11
|
+
$stderr.puts cli_output
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rubycom
|
2
|
+
module Executor
|
3
|
+
|
4
|
+
# Calls the given method with the given parameters
|
5
|
+
#
|
6
|
+
# @param [Method] method the Method to call
|
7
|
+
# @param [Hash] parameters a Hash mapping parameter names to their intended values
|
8
|
+
# @return the result of the Method call
|
9
|
+
def self.execute_command(method, parameters={})
|
10
|
+
raise "#{method} should be a Method but was #{method.class}" if method.class != Method
|
11
|
+
raise "#{parameters} should be a Hash but was #{parameters.class}" if parameters.class != Hash
|
12
|
+
params = method.parameters.reject{|type,_|type == :rest}.map { |_, sym|
|
13
|
+
raise ExecutorError, "parameters should include values for all non * method parameters. Missing value for #{sym.to_s}" unless parameters.has_key?(sym)
|
14
|
+
parameters[sym]
|
15
|
+
}
|
16
|
+
unless method.parameters.select{|type,_|type == :rest}.first.nil? #if there is a * param
|
17
|
+
params = params + parameters[method.parameters.select{|type,_|type == :rest}.first[1]] #add in the values which were marked for the * param
|
18
|
+
end
|
19
|
+
(parameters.nil? || parameters.empty?) ? method.call : method.call(*params)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|