boson 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +1 -1
- data/README.rdoc +2 -2
- data/Rakefile +2 -2
- data/VERSION.yml +1 -1
- data/lib/boson.rb +1 -1
- data/lib/boson/command.rb +12 -2
- data/lib/boson/commands/core.rb +16 -11
- data/lib/boson/commands/web_core.rb +107 -27
- data/lib/boson/inspector.rb +3 -9
- data/lib/boson/inspectors/method_inspector.rb +2 -2
- data/lib/boson/libraries/file_library.rb +6 -6
- data/lib/boson/manager.rb +1 -1
- data/lib/boson/option_command.rb +4 -1
- data/lib/boson/option_parser.rb +49 -21
- data/lib/boson/options.rb +18 -9
- data/lib/boson/pipe.rb +79 -89
- data/lib/boson/pipes.rb +67 -0
- data/lib/boson/repo.rb +13 -1
- data/lib/boson/runners/bin_runner.rb +6 -5
- data/lib/boson/scientist.rb +15 -12
- data/lib/boson/util.rb +19 -16
- data/lib/boson/view.rb +4 -1
- data/test/argument_inspector_test.rb +1 -1
- data/test/method_inspector_test.rb +2 -2
- data/test/option_parser_test.rb +2 -190
- data/test/options_test.rb +189 -0
- data/test/{pipe_test.rb → pipes_test.rb} +10 -14
- data/test/scientist_test.rb +4 -0
- data/test/test_helper.rb +11 -0
- data/test/util_test.rb +58 -0
- metadata +10 -5
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
@@ -18,8 +18,8 @@ Note: To read a linkable version of this README, {see here}[http://tagaholic.me/
|
|
18
18
|
* can have any number of local and global options (Boson::OptionCommand). Options are defined with Boson::OptionParser.
|
19
19
|
* can have any view associated to it (via Hirb) without adding view code to the command's method.
|
20
20
|
These views can be toggled on and manipulated via global render options (Boson::View and Boson::OptionCommand).
|
21
|
-
* can pipe their return value into
|
22
|
-
|
21
|
+
* can pipe their return value into custom pipe options (Boson::Pipe).
|
22
|
+
* has default pipe options to search and sort an array of any objects (Boson::Pipes).
|
23
23
|
* Option parser (Boson::OptionParser)
|
24
24
|
* provides option types that map to objects i.e. :array type creates Array objects.
|
25
25
|
* come with 5 default option types: boolean, array, string, hash and numeric.
|
data/Rakefile
CHANGED
@@ -25,14 +25,14 @@ begin
|
|
25
25
|
s.authors = ["Gabriel Horner"]
|
26
26
|
s.has_rdoc = true
|
27
27
|
s.rubyforge_project = 'tagaholic'
|
28
|
-
s.add_dependency 'hirb', '>= 0.2.
|
28
|
+
s.add_dependency 'hirb', '>= 0.2.10'
|
29
29
|
s.add_dependency 'alias', '>= 0.2.1'
|
30
30
|
s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
|
31
31
|
s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "LICENSE.txt", "{bin,lib,test}/**/*"]
|
32
32
|
end
|
33
33
|
|
34
34
|
rescue LoadError
|
35
|
-
puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install
|
35
|
+
puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install jeweler"
|
36
36
|
end
|
37
37
|
|
38
38
|
Rake::TestTask.new do |t|
|
data/VERSION.yml
CHANGED
data/lib/boson.rb
CHANGED
@@ -5,7 +5,7 @@ $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirna
|
|
5
5
|
# order of library subclasses matters
|
6
6
|
%w{module file gem require local_file}.each {|e| require "boson/libraries/#{e}_library" }
|
7
7
|
(%w{namespace view command util commands option_parser options} +
|
8
|
-
%w{index repo_index scientist option_command pipe}).each {|e| require "boson/#{e}" }
|
8
|
+
%w{index repo_index scientist option_command pipe pipes}).each {|e| require "boson/#{e}" }
|
9
9
|
|
10
10
|
# This module stores the libraries, commands, repos and main object used throughout Boson.
|
11
11
|
#
|
data/lib/boson/command.rb
CHANGED
@@ -72,7 +72,7 @@ module Boson
|
|
72
72
|
@args = [['*args']]
|
73
73
|
end
|
74
74
|
end
|
75
|
-
@config =
|
75
|
+
@config = Util.recursive_hash_merge hash, hash.delete(:config) || {}
|
76
76
|
end
|
77
77
|
|
78
78
|
# Library object a command belongs to.
|
@@ -96,6 +96,11 @@ module Boson
|
|
96
96
|
@option_parser ||= OptionParser.new(@options || {})
|
97
97
|
end
|
98
98
|
|
99
|
+
# Option parser for command as defined by @render_options.
|
100
|
+
def render_option_parser
|
101
|
+
option_command? ? Boson::Scientist.option_command(self).option_parser : nil
|
102
|
+
end
|
103
|
+
|
99
104
|
# Help string for options if a command has it.
|
100
105
|
def option_help
|
101
106
|
@options ? option_parser.to_s : ''
|
@@ -120,6 +125,11 @@ module Boson
|
|
120
125
|
end
|
121
126
|
|
122
127
|
#:stopdoc:
|
128
|
+
# until @config is consistent in index + actual loading
|
129
|
+
def config
|
130
|
+
@config ||= {}
|
131
|
+
end
|
132
|
+
|
123
133
|
def file_string_and_method_for_args(lib)
|
124
134
|
if !lib.is_a?(ModuleLibrary) && (klass_method = (lib.class_commands || {})[@name])
|
125
135
|
if RUBY_VERSION >= '1.9'
|
@@ -135,7 +145,7 @@ module Boson
|
|
135
145
|
end
|
136
146
|
|
137
147
|
def has_splat_args?
|
138
|
-
!!(
|
148
|
+
!!(args && @args[-1] && @args[-1][0][/^\*/])
|
139
149
|
end
|
140
150
|
|
141
151
|
def make_option_command(lib=library)
|
data/lib/boson/commands/core.rb
CHANGED
@@ -8,13 +8,16 @@ module Boson::Commands::Core #:nodoc:
|
|
8
8
|
commands = {
|
9
9
|
'render'=>{:desc=>"Render any object using Hirb"},
|
10
10
|
'menu'=>{:desc=>"Provide a menu to multi-select elements from a given array"},
|
11
|
-
'usage'=>{:desc=>"Print a command's usage", :options=>{
|
11
|
+
'usage'=>{:desc=>"Print a command's usage", :options=>{
|
12
|
+
:verbose=>{:desc=>"Display global options", :type=>:boolean},
|
13
|
+
:render_options=>{:desc=>"Render options for option tables", :default=>{},
|
14
|
+
:keys=>[:vertical, :fields, :hide_empty]} } },
|
12
15
|
'commands'=>{
|
13
16
|
:desc=>"List or search commands. Query must come before any options.", :default_option=>'query',
|
14
17
|
:options=>{ :index=>{:type=>:boolean, :desc=>"Searches index"},
|
15
18
|
:local=>{:type=>:boolean, :desc=>"Local commands only" } },
|
16
19
|
:render_options=>{
|
17
|
-
:headers=>{:default=>{:desc=>'description'}},
|
20
|
+
[:headers,:H]=>{:default=>{:desc=>'description'}},
|
18
21
|
:query=>{:keys=>command_attributes, :default_keys=>'full_name'},
|
19
22
|
:fields=>{:default=>[:full_name, :lib, :alias, :usage, :desc], :values=>command_attributes, :enum=>false},
|
20
23
|
:filters=>{:default=>{:render_options=>:inspect, :options=>:inspect, :args=>:inspect, :config=>:inspect}}
|
@@ -53,20 +56,22 @@ module Boson::Commands::Core #:nodoc:
|
|
53
56
|
Boson::View.render(object, options)
|
54
57
|
end
|
55
58
|
|
56
|
-
def menu(
|
57
|
-
Hirb::Console.format_output(
|
59
|
+
def menu(arr, options={}, &block)
|
60
|
+
Hirb::Console.format_output(arr, options.merge(:class=>"Hirb::Menu"), &block)
|
58
61
|
end
|
59
62
|
|
60
63
|
def usage(command, options={})
|
61
|
-
msg = (cmd = Boson::Command.find(command)) ? "#{command} #{cmd.usage}" : "Command '#{
|
64
|
+
msg = (cmd = Boson::Command.find(command)) ? "#{command} #{cmd.usage}" : "Command '#{command}' not found"
|
62
65
|
puts msg
|
63
|
-
if
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
66
|
+
return if options[:one_line] || !cmd
|
67
|
+
|
68
|
+
if cmd.options && !cmd.options.empty?
|
69
|
+
puts "\nLOCAL OPTIONS"
|
70
|
+
cmd.option_parser.print_usage_table options[:render_options].dup.merge(:local=>true)
|
71
|
+
end
|
72
|
+
if options[:verbose]
|
68
73
|
puts "\nGLOBAL OPTIONS"
|
69
|
-
|
74
|
+
cmd.render_option_parser.print_usage_table options[:render_options].dup
|
70
75
|
end
|
71
76
|
end
|
72
77
|
end
|
@@ -1,52 +1,59 @@
|
|
1
|
-
module Boson::Commands::WebCore
|
1
|
+
module Boson::Commands::WebCore
|
2
2
|
extend self
|
3
3
|
|
4
|
-
def config
|
4
|
+
def config #:nodoc:
|
5
5
|
descriptions = {
|
6
6
|
:install=>"Installs a library by url. Library should then be loaded with load_library.",
|
7
|
-
:browser=>"Opens urls in a browser on a Mac", :get=>"Gets the body of a url", :post=>'Posts to url'
|
7
|
+
:browser=>"Opens urls in a browser on a Mac", :get=>"Gets the body of a url", :post=>'Posts to url',
|
8
|
+
:build_url=>"Builds a url, escaping the given params"
|
9
|
+
}
|
8
10
|
commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:desc=>v}; h}
|
9
11
|
commands['install'][:options] = {:name=>{:type=>:string, :desc=>"Library name to save to"},
|
10
12
|
:force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
|
13
|
+
:default=>{:type=>:boolean, :desc=>'Adds library as a default library to main config file'},
|
11
14
|
:module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
|
12
15
|
:method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
|
16
|
+
commands['install'][:args] = [['url'],['options', {}]]
|
13
17
|
{:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
|
14
18
|
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
res.code == '200' ? res.body : nil
|
22
|
-
else
|
23
|
-
Net::HTTP.get(URI.parse(url))
|
20
|
+
# Requires libraries only once before defining method with given block
|
21
|
+
def self.def_which_requires(meth, *libs, &block)
|
22
|
+
define_method(meth) do |*args|
|
23
|
+
libs.each {|e| require e }
|
24
|
+
define_method(meth, block).call(*args)
|
24
25
|
end
|
25
|
-
rescue
|
26
|
-
raise "Error opening #{url}"
|
27
26
|
end
|
28
27
|
|
29
|
-
|
30
|
-
|
28
|
+
def_which_requires(:get, 'uri', 'net/http') do |url, options|
|
29
|
+
options ||= {}
|
30
|
+
url = build_url(url, options[:params]) if options[:params]
|
31
|
+
Get.new(url).request(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def_which_requires(:build_url, 'cgi') do |url, params|
|
35
|
+
url + (url[/\?/] ? '&' : "?") + params.map {|k,v|
|
36
|
+
v = v.is_a?(Array) ? v.join(' ') : v.to_s
|
37
|
+
"#{k}=#{CGI.escape(v)}"
|
38
|
+
}.join("&")
|
39
|
+
end
|
40
|
+
|
41
|
+
def_which_requires(:post, 'uri', 'net/http') do |url, options|
|
31
42
|
Net::HTTP.post_form(URI.parse(url), options)
|
32
43
|
end
|
33
44
|
|
34
|
-
def install(url, options={})
|
45
|
+
def install(url, options={}) #:nodoc:
|
35
46
|
options[:name] ||= strip_name_from_url(url)
|
36
47
|
return puts("Please give a library name for this url.") if options[:name].empty?
|
37
|
-
filename = File.join Boson.repo.commands_dir, "#{options[:name]}.rb"
|
48
|
+
filename = File.join ::Boson.repo.commands_dir, "#{options[:name]}.rb"
|
38
49
|
return puts("Library name #{options[:name]} already exists. Try a different name.") if File.exists?(filename) && !options[:force]
|
39
|
-
File.open(filename, 'w') {|f| f.write get(url) }
|
40
50
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
file_string = "module #{mod_name}\n#{file_string}\nend"
|
48
|
-
File.open(filename, 'w') {|f| f.write file_string }
|
49
|
-
end
|
51
|
+
file_string = get(url)
|
52
|
+
file_string = "# Originally from #{url}\n"+file_string
|
53
|
+
file_string = wrap_install(file_string, options) if options[:method_wrap] || options[:module_wrap]
|
54
|
+
|
55
|
+
File.open(filename, 'w') {|f| f.write file_string }
|
56
|
+
Boson.repo.update_config {|c| (c[:defaults] ||= []) << options[:name] } if options[:default]
|
50
57
|
puts "Saved to #{filename}."
|
51
58
|
end
|
52
59
|
|
@@ -56,7 +63,80 @@ module Boson::Commands::WebCore #:nodoc:
|
|
56
63
|
end
|
57
64
|
|
58
65
|
private
|
66
|
+
def wrap_install(file_string, options)
|
67
|
+
indent = " "
|
68
|
+
unless (mod_name = ::Boson::Util.camelize(options[:name]))
|
69
|
+
return puts("Can't wrap install with name #{options[:name]}")
|
70
|
+
end
|
71
|
+
|
72
|
+
file_string.gsub!(/(^)/,'\1'+indent)
|
73
|
+
file_string = "def #{options[:name]}\n#{file_string}\nend".gsub(/(^)/,'\1'+indent) if options[:method_wrap]
|
74
|
+
"module #{mod_name}\n#{file_string}\nend"
|
75
|
+
end
|
76
|
+
|
59
77
|
def strip_name_from_url(url)
|
60
78
|
url[/\/([^\/.]+)(\.[a-z]+)?$/, 1].to_s.gsub('-', '_').gsub(/[^a-zA-Z_]/, '')
|
61
79
|
end
|
80
|
+
|
81
|
+
# Used by the get command to make get requests and optionally parse json and yaml.
|
82
|
+
# Ruby 1.8.x is dependent on json gem for parsing json.
|
83
|
+
# See Get.request for options a request can take.
|
84
|
+
class Get
|
85
|
+
FORMAT_HEADERS = {
|
86
|
+
:json=>%w{application/json text/json application/javascript text/javascript},
|
87
|
+
:yaml=>%w{application/x-yaml text/yaml}
|
88
|
+
} #:nodoc:
|
89
|
+
|
90
|
+
def initialize(url, options={})
|
91
|
+
@url, @options = url, options
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the response body string or a parsed data structure. Returns nil if request fails. By default expects response
|
95
|
+
# to be 200.
|
96
|
+
# ==== Options:
|
97
|
+
# [:any_response] Returns body string for any response code. Default is false.
|
98
|
+
# [:parse] Parse the body into either json or yaml. Expects a valid format or if true autodetects one.
|
99
|
+
# Default is false.
|
100
|
+
# [:raise_error] Raises any original errors when parsing or fetching url instead of handling errors silently.
|
101
|
+
def request(options={})
|
102
|
+
@options.merge! options
|
103
|
+
body = get_body
|
104
|
+
body && @options[:parse] ? parse_body(body) : body
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
# Returns body string if successful or nil if not.
|
109
|
+
def get_body
|
110
|
+
uri = URI.parse(@url)
|
111
|
+
@response = Net::HTTP.get_response uri
|
112
|
+
(@options[:any_response] || @response.code == '200') ? @response.body : nil
|
113
|
+
rescue
|
114
|
+
@options[:raise_error] ? raise : puts("Error: GET '#{@url}' -> #{$!.class}: #{$!.message}")
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns nil if dependencies or parsing fails
|
118
|
+
def parse_body(body)
|
119
|
+
format = determine_format(@options[:parse])
|
120
|
+
case format
|
121
|
+
when :json
|
122
|
+
unless ::Boson::Util.safe_require 'json'
|
123
|
+
return puts("Install the json gem to parse json: sudo gem install json")
|
124
|
+
end
|
125
|
+
JSON.parse body
|
126
|
+
when :yaml
|
127
|
+
YAML::load body
|
128
|
+
else
|
129
|
+
puts "Can't parse this format."
|
130
|
+
end
|
131
|
+
rescue
|
132
|
+
@options[:raise_error] ? raise : puts("Error while parsing #{format} response of '#{@url}': #{$!.class}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def determine_format(format)
|
136
|
+
return format.to_sym if %w{json yaml}.include?(format.to_s)
|
137
|
+
return :json if FORMAT_HEADERS[:json].include?(@response.content_type)
|
138
|
+
return :yaml if FORMAT_HEADERS[:yaml].include?(@response.content_type)
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
62
142
|
end
|
data/lib/boson/inspector.rb
CHANGED
@@ -68,9 +68,8 @@ module Boson
|
|
68
68
|
|
69
69
|
#:stopdoc:
|
70
70
|
def add_method_scraped_data
|
71
|
-
(MethodInspector::METHODS + [:
|
72
|
-
key
|
73
|
-
(@store[e] || []).each do |cmd, val|
|
71
|
+
(MethodInspector::METHODS + [:args]).each do |key|
|
72
|
+
(@store[key] || []).each do |cmd, val|
|
74
73
|
@commands_hash[cmd] ||= {}
|
75
74
|
add_scraped_data_to_config(key, val, cmd)
|
76
75
|
end
|
@@ -94,15 +93,10 @@ module Boson
|
|
94
93
|
scraped = CommentInspector.scrape(FileLibrary.read_library_file(file), lineno, MethodInspector.current_module)
|
95
94
|
@commands_hash[cmd] ||= {}
|
96
95
|
MethodInspector::METHODS.each do |e|
|
97
|
-
add_scraped_data_to_config(
|
96
|
+
add_scraped_data_to_config(e, scraped[e], cmd)
|
98
97
|
end
|
99
98
|
end
|
100
99
|
end
|
101
|
-
|
102
|
-
# translates from inspector attribute name to command attribute name
|
103
|
-
def command_key(key)
|
104
|
-
{:method_args=>:args}[key] || key
|
105
|
-
end
|
106
100
|
#:startdoc:
|
107
101
|
end
|
108
102
|
end
|
@@ -37,13 +37,13 @@ module Boson
|
|
37
37
|
|
38
38
|
# Scrapes a method's arguments using ArgumentInspector.
|
39
39
|
def scrape_arguments(meth)
|
40
|
-
store[:
|
40
|
+
store[:args] ||= {}
|
41
41
|
|
42
42
|
o = Object.new
|
43
43
|
o.extend(@current_module)
|
44
44
|
# private methods return nil
|
45
45
|
if (val = ArgumentInspector.scrape_with_eval(meth, @current_module, o))
|
46
|
-
store[:
|
46
|
+
store[:args][meth.to_s] = val
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -18,8 +18,8 @@ module Boson
|
|
18
18
|
# end
|
19
19
|
#
|
20
20
|
# Once loaded, this library can be run from the commandline or irb:
|
21
|
-
#
|
22
|
-
#
|
21
|
+
# $ boson take_over world
|
22
|
+
# >> take_over 'world'
|
23
23
|
#
|
24
24
|
# If the library is namespaced, the command would be run as brain.take_over.
|
25
25
|
#
|
@@ -33,8 +33,8 @@ module Boson
|
|
33
33
|
# end
|
34
34
|
#
|
35
35
|
# From the commandline and irb this runs as:
|
36
|
-
#
|
37
|
-
#
|
36
|
+
# $ boson take_over world -e initiate_brainiac
|
37
|
+
# >> take_over 'world -e initiate_brainiac'
|
38
38
|
#
|
39
39
|
# Since Boson aims to make your libraries just standard ruby, we can achieve the above
|
40
40
|
# by making options a commented method attribute:
|
@@ -53,11 +53,11 @@ module Boson
|
|
53
53
|
# * See Inspector for other method attributes, like config and render_options, that can be placed above a method.
|
54
54
|
#
|
55
55
|
# Once a command has a defined option, a command can also recognize a slew of global options:
|
56
|
-
#
|
56
|
+
# >> take_over '-h'
|
57
57
|
# take_over [destination] [--execute=STRING]
|
58
58
|
#
|
59
59
|
# # prints much more verbose help
|
60
|
-
#
|
60
|
+
# >> take_over '-hv'
|
61
61
|
#
|
62
62
|
# For more about these global options see OptionCommand and View.
|
63
63
|
class FileLibrary < Library
|
data/lib/boson/manager.rb
CHANGED
@@ -44,7 +44,7 @@ module Boson
|
|
44
44
|
FileLibrary.reset_file_cache(library.to_s)
|
45
45
|
failed_libraries << library
|
46
46
|
$stderr.puts "Unable to #{load_method} library #{library}. Reason: #{e.message}"
|
47
|
-
rescue
|
47
|
+
rescue StandardError=>e
|
48
48
|
FileLibrary.reset_file_cache(library.to_s)
|
49
49
|
failed_libraries << library
|
50
50
|
message = "Unable to #{load_method} library #{library}. Reason: #{$!}"
|
data/lib/boson/option_command.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'shellwords'
|
2
2
|
module Boson
|
3
3
|
# A class used by Scientist to wrap around Command objects. It's main purpose is to parse
|
4
|
-
# a command's global options (basic
|
4
|
+
# a command's global options (basic, render and pipe types) and local options.
|
5
5
|
# As the names imply, global options are available to all commands while local options are specific to a command.
|
6
6
|
# When passing options to commands, global ones _must_ be passed first, then local ones.
|
7
|
+
# Also, options _must_ all be passed either before or after arguments.
|
7
8
|
# For more about pipe and render options see Pipe and View respectively.
|
8
9
|
#
|
9
10
|
# === Basic Global Options
|
@@ -61,6 +62,7 @@ module Boson
|
|
61
62
|
:help=>{:type=>:boolean, :desc=>"Display a command's help"},
|
62
63
|
:render=>{:type=>:boolean, :desc=>"Toggle a command's default rendering behavior"},
|
63
64
|
:verbose=>{:type=>:boolean, :desc=>"Increase verbosity for help, errors, etc."},
|
65
|
+
:usage_options=>{:type=>:string, :desc=>"Render options to pass to usage/help"},
|
64
66
|
:pretend=>{:type=>:boolean, :desc=>"Display what a command would execute without executing it"},
|
65
67
|
:delete_options=>{:type=>:array, :desc=>'Deletes global options starting with given strings' }
|
66
68
|
} #:nodoc:
|
@@ -76,6 +78,7 @@ module Boson
|
|
76
78
|
:sort=>{:type=>:string, :desc=>"Sort by given field"},
|
77
79
|
:reverse_sort=>{:type=>:boolean, :desc=>"Reverse a given sort"},
|
78
80
|
:query=>{:type=>:hash, :desc=>"Queries fields given field:search pairs"},
|
81
|
+
:pipes=>{:alias=>'P', :type=>:array, :desc=>"Pipe to commands sequentially"}
|
79
82
|
} #:nodoc:
|
80
83
|
|
81
84
|
class <<self
|
data/lib/boson/option_parser.rb
CHANGED
@@ -77,6 +77,7 @@ module Boson
|
|
77
77
|
EQ_RE = /^(--\w+[-\w+]*|-[a-zA-Z])=(.*)$/i
|
78
78
|
SHORT_SQ_RE = /^-([a-zA-Z]{2,})$/i # Allow either -x -v or -xv style for single char args
|
79
79
|
SHORT_NUM = /^(-[a-zA-Z])#{NUMERIC}$/i
|
80
|
+
STOP_STRINGS = %w{-- -}
|
80
81
|
|
81
82
|
attr_reader :leading_non_opts, :trailing_non_opts, :opt_aliases
|
82
83
|
|
@@ -148,9 +149,9 @@ module Boson
|
|
148
149
|
# Default is false.
|
149
150
|
# [*:alias*] Alternative way to define option aliases with an option name or an array of them. Useful in yaml files.
|
150
151
|
# Setting to false will prevent creating an automatic alias.
|
151
|
-
# [*:values*] An array of values an option can have. Available for :array and :string options.
|
152
|
-
# can be aliased by typing a unique string it starts with
|
153
|
-
# f refers to foo,
|
152
|
+
# [*:values*] An array of values an option can have. Available for :array and :string options. Values here
|
153
|
+
# can be aliased by typing a unique string it starts with or underscore aliasing (see Util.underscore_search).
|
154
|
+
# For example, for values foo, odd and obnoxiously_long, f refers to foo, od to odd and o_l to obnoxiously_long.
|
154
155
|
# [*:enum*] Boolean indicating if an option enforces values in :values or :keys. Default is true. For
|
155
156
|
# :array, :hash and :string options.
|
156
157
|
# [*:split*] For :array and :hash options. A string or regular expression on which an array value splits
|
@@ -196,8 +197,7 @@ module Boson
|
|
196
197
|
case type
|
197
198
|
when TrueClass then @defaults[nice_name] = true
|
198
199
|
when FalseClass then @defaults[nice_name] = false
|
199
|
-
else
|
200
|
-
@defaults[nice_name] = type unless type.is_a?(Symbol)
|
200
|
+
else @defaults[nice_name] = type unless type.is_a?(Symbol)
|
201
201
|
end
|
202
202
|
mem[name] = !type.nil? ? determine_option_type(type) : type
|
203
203
|
mem
|
@@ -207,7 +207,7 @@ module Boson
|
|
207
207
|
@opt_aliases = @opt_aliases.sort.inject({}) {|h, (nice_name, aliases)|
|
208
208
|
name = dasherize nice_name
|
209
209
|
# allow for aliases as symbols
|
210
|
-
aliases.map! {|e| e.to_s.index('-')
|
210
|
+
aliases.map! {|e| e.to_s.index('-') == 0 || e == false ? e : dasherize(e.to_s) }
|
211
211
|
if aliases.empty? and nice_name.length > 1
|
212
212
|
opt_alias = nice_name[0,1]
|
213
213
|
opt_alias = h.key?("-"+opt_alias) ? "-"+opt_alias.capitalize : "-"+opt_alias
|
@@ -232,7 +232,7 @@ module Boson
|
|
232
232
|
|
233
233
|
@leading_non_opts = []
|
234
234
|
unless flags[:opts_before_args]
|
235
|
-
@leading_non_opts << shift until current_is_option? || @args.empty? || peek
|
235
|
+
@leading_non_opts << shift until current_is_option? || @args.empty? || STOP_STRINGS.include?(peek)
|
236
236
|
end
|
237
237
|
|
238
238
|
while current_is_option?
|
@@ -281,20 +281,41 @@ module Boson
|
|
281
281
|
|
282
282
|
# More verbose option help in the form of a table.
|
283
283
|
def print_usage_table(render_options={})
|
284
|
+
user_fields = render_options.delete(:fields)
|
285
|
+
fields = get_usage_fields user_fields
|
286
|
+
(fields << :default).uniq! if render_options.delete(:local) || user_fields == '*'
|
287
|
+
opts = all_options_with_fields fields
|
288
|
+
fields.delete(:default) if fields.include?(:default) && opts.all? {|e| e[:default].nil? }
|
289
|
+
render_options = default_render_options.merge(:fields=>fields).merge(render_options)
|
290
|
+
View.render opts, render_options
|
291
|
+
end
|
292
|
+
|
293
|
+
def all_options_with_fields(fields) #:nodoc:
|
284
294
|
aliases = @opt_aliases.invert
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
h =
|
289
|
-
|
290
|
-
h[f] =
|
295
|
+
@opt_types.keys.sort.inject([]) {|t,e|
|
296
|
+
nice_name = undasherize(e)
|
297
|
+
h = {:name=>e, :type=>@opt_types[e], :alias=>aliases[e] || '' }
|
298
|
+
h[:default] = @defaults[nice_name] if fields.include?(:default)
|
299
|
+
(fields - h.keys).each {|f|
|
300
|
+
h[f] = (option_attributes[nice_name] || {})[f]
|
291
301
|
}
|
292
302
|
t << h
|
293
303
|
}
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
304
|
+
end
|
305
|
+
|
306
|
+
def default_render_options #:nodoc:
|
307
|
+
{:header_filter=>:capitalize, :description=>false, :filter_any=>true,
|
308
|
+
:filter_classes=>{Array=>[:join, ',']}, :hide_empty=>true}
|
309
|
+
end
|
310
|
+
|
311
|
+
# Hash of option names mapped to hash of its external attributes
|
312
|
+
def option_attributes
|
313
|
+
@option_attributes || {}
|
314
|
+
end
|
315
|
+
|
316
|
+
def get_usage_fields(fields) #:nodoc:
|
317
|
+
fields || ([:name, :alias, :type] + [:desc, :values, :keys].select {|e|
|
318
|
+
option_attributes.values.any? {|f| f.key?(e) } }).uniq
|
298
319
|
end
|
299
320
|
|
300
321
|
# Hash of option attributes for the currently parsed option. _Any_ hash keys
|
@@ -343,8 +364,7 @@ module Boson
|
|
343
364
|
case value
|
344
365
|
when TrueClass, FalseClass then :boolean
|
345
366
|
when Numeric then :numeric
|
346
|
-
else
|
347
|
-
Util.underscore(value.class.to_s).to_sym
|
367
|
+
else Util.underscore(value.class.to_s).to_sym
|
348
368
|
end
|
349
369
|
end
|
350
370
|
|
@@ -365,7 +385,15 @@ module Boson
|
|
365
385
|
end
|
366
386
|
|
367
387
|
def auto_alias_value(values, possible_value)
|
368
|
-
|
388
|
+
if Boson.repo.config[:option_underscore_search]
|
389
|
+
self.class.send(:define_method, :auto_alias_value) {|values, possible_value|
|
390
|
+
Util.underscore_search(possible_value, values, true) || possible_value
|
391
|
+
}.call(values, possible_value)
|
392
|
+
else
|
393
|
+
self.class.send(:define_method, :auto_alias_value) {|values, possible_value|
|
394
|
+
values.find {|v| v.to_s =~ /^#{possible_value}/ } || possible_value
|
395
|
+
}.call(values, possible_value)
|
396
|
+
end
|
369
397
|
end
|
370
398
|
|
371
399
|
def validate_enum_values(values, possible_values)
|
@@ -386,7 +414,7 @@ module Boson
|
|
386
414
|
|
387
415
|
def delete_invalid_opts
|
388
416
|
@trailing_non_opts.delete_if {|e|
|
389
|
-
break if
|
417
|
+
break if STOP_STRINGS.include? e
|
390
418
|
invalid = e.to_s[/^-/]
|
391
419
|
$stderr.puts "Deleted invalid option '#{e}'" if invalid
|
392
420
|
invalid
|