rubycom 0.3.2 → 0.4.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.
- 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
|