boson 0.2.2 → 0.2.3
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.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
|