boson 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,76 @@
1
+ == 0.2.4
2
+ * Tests use bacon and pass on all major ruby versions
3
+ * Fixed bug for 1.8.7 and super (#10)
4
+ * Added commandline pipes with '+'
5
+ * Fixed bug when requiring rubygems in a library
6
+ * Fixed bug in sort pipe for 1.9.2
7
+ * Got rid of jeweler in Rakefile and $LOAD_PATH meddling
8
+ * Refactored BinRunner's error handling
9
+
10
+ == 0.2.3
11
+ * Added improved support and additional attributes for user pipe options
12
+ * Added :pipes global command option
13
+ * Added json + yaml parsing to get()
14
+ * Added underscore searching to option values
15
+ * Added build_install() command
16
+ * Added :usage_options commandline option
17
+
18
+ == 0.2.2
19
+ * Renamed Boson::Command#description to #desc. Delete your index at ~/.boson/config/index.marshal.
20
+ * Renamed Boson::Command#global_options to #option_command. Update your configs.
21
+ * Bug fix for Windows and indexing (#4)
22
+ * Added system wide Boson commands at /etc/boson (#2)
23
+ * Added Boson::Command#config for plugins to set/get via @config
24
+ * Added option_command and unload options to BinRunner
25
+ * Added special option parsing characters: - and --
26
+ * Added special :output_class key for global render_options
27
+ * Added :delete_options global option
28
+ * Fixed --no variant for single letter booleans
29
+ * Fixed MethodInspector parsing arguments with special characters
30
+ * Allow global -p to work even in failures
31
+ * Allow -hv to default to verbose help
32
+ * Boson::OptionParser tweaks
33
+
34
+ == 0.2.1
35
+ * Added local libraries: Bosonfile and under local repositories
36
+ * Added config method attribute.
37
+ * Added default_option and global_options command attributes.
38
+ * Added OptionParser.parse and OptionParser.usage for scripting use.
39
+ * Improved auto-rendering from commandline.
40
+ * Removed library reload.
41
+ * Better docs.
42
+
43
+ == 0.2.0
44
+ * Command options
45
+ ** Added custom global and render options for commands.
46
+ ** Added pipe and filter option commands.
47
+ ** Add global query option.
48
+ * Options
49
+ ** Users can define custom option types.
50
+ ** Added hash option type.
51
+ ** Any option can be a boolean with :bool_default attribute.
52
+ ** Adding * aliasing to relevant options.
53
+ * Made Boson::Scientist.commandify for use outside Boson.
54
+ * Any command can have a default option.
55
+ * Directories are namespaced automatically.
56
+ * Solidified library module callback methods.
57
+ * Added support for Windows home.
58
+ * Improved ModuleLibrary to handle class or module class methods.
59
+ * Better search and sort integration with Hirb.
60
+ * Better docs.
61
+ * Fixed number of bugs.
62
+ * query_fields option for searching libraries and commands is deprecated. Specifying query
63
+ fields is now done by prefixing a query with ':'. For example:
64
+ bash> boson commands library_type:gem
65
+ # instead of
66
+ bash> boson commands gem --query_fields=library_type
67
+
68
+ == 0.1.0
69
+ * First real release
70
+ * Plenty of fixes to make it ruby 1.9 ready.
71
+ * Added more documentation
72
+ * BinRunner tweaks and bug fixes
73
+ * Other miscellaneous bug fixes
74
+
75
+ == 0.0.1
76
+ * An initial release for others to play with.
data/README.rdoc CHANGED
@@ -1,9 +1,9 @@
1
+ To read a linkable version of this README, {see here}[http://tagaholic.me/boson/doc/].
2
+
1
3
  == Description
2
4
  A command/task framework similar to rake and thor that opens your ruby universe to the commandline
3
5
  and irb. For my libraries that use this, see {irbfiles}[http://github.com/cldwalker/irbfiles].
4
- Works with Ruby 1.8.6 and 1.9.1.
5
-
6
- Note: To read a linkable version of this README, {see here}[http://tagaholic.me/boson/doc/].
6
+ Works with all major ruby versions.
7
7
 
8
8
  == Features
9
9
  * Simple organization: Commands are just methods on an object (default is main) and command libraries are just modules.
@@ -161,8 +161,8 @@ My {tagging obsession}[http://github.com/cldwalker/tag-tree] from the ruby conso
161
161
 
162
162
  == Acknowledgements
163
163
  Boson stands on the shoulders of these people and their ideas:
164
- * Yehuda Katz for inspiring me with Thor's power and elegant design
165
- * Yehuda Katz and Daniel Berger for an awesome option parser (Boson::OptionParser)
164
+ * Yehuda Katz for inspiring me with Thor and its awesome option parser (Boson::OptionParser).
165
+ * Daniel Berger for his original work on thor's awesome option parser.
166
166
  * Dave Thomas for scraping a method's comments (Boson::CommentInspector)
167
167
  * Mauricio Fernandez for scraping a method's arguments (Boson::ArgumentInspector)
168
- * Chris Wanstrath for inspiring Boson's libraries with Rip's packages.
168
+ * Chris Wanstrath for inspiring Boson's libraries with Rip's packages.
data/Rakefile CHANGED
@@ -1,52 +1,35 @@
1
1
  require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
- begin
5
- require 'rcov/rcovtask'
2
+ require 'fileutils'
6
3
 
7
- Rcov::RcovTask.new do |t|
8
- t.libs << 'test'
9
- t.test_files = FileList['test/**/*_test.rb']
10
- t.rcov_opts = ["-T -x '/Library/Ruby/*'"]
11
- t.verbose = true
12
- end
13
- rescue LoadError
14
- puts "Rcov not available. Install it for rcov-related tasks with: sudo gem install rcov"
4
+ def gemspec
5
+ @gemspec ||= eval(File.read('gemspec'), binding, 'gemspec')
15
6
  end
16
7
 
17
- begin
18
- require 'jeweler'
19
- Jeweler::Tasks.new do |s|
20
- s.name = "boson"
21
- s.description = "A command/task framework similar to rake and thor that opens your ruby universe to the commandline and irb."
22
- s.summary = "Boson provides users with the power to turn any ruby method into a full-fledged commandline tool. Boson achieves this with powerful options (borrowed from thor) and views (thanks to hirb). Some other unique features that differentiate it from rake and thor include being accessible from irb and the commandline, being able to write boson commands in non-dsl ruby and toggling a pretty view of a command's output without additional view code."
23
- s.email = "gabriel.horner@gmail.com"
24
- s.homepage = "http://tagaholic.me/boson/"
25
- s.authors = ["Gabriel Horner"]
26
- s.has_rdoc = true
27
- s.rubyforge_project = 'tagaholic'
28
- s.add_dependency 'hirb', '>= 0.2.10'
29
- s.add_dependency 'alias', '>= 0.2.1'
30
- s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
31
- s.files = FileList["Rakefile", "VERSION.yml", "README.rdoc", "LICENSE.txt", "{bin,lib,test}/**/*"]
32
- end
8
+ desc "Build the gem"
9
+ task :gem=>:gemspec do
10
+ sh "gem build gemspec"
11
+ FileUtils.mkdir_p 'pkg'
12
+ FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
13
+ end
14
+
15
+ desc "Install the gem locally"
16
+ task :install => :gem do
17
+ sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
18
+ end
33
19
 
34
- rescue LoadError
35
- puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install jeweler"
20
+ desc "Generate the gemspec"
21
+ task :generate do
22
+ puts gemspec.to_ruby
36
23
  end
37
24
 
38
- Rake::TestTask.new do |t|
39
- t.libs << 'lib'
40
- t.pattern = 'test/**/*_test.rb'
41
- t.verbose = false
25
+ desc "Validate the gemspec"
26
+ task :gemspec do
27
+ gemspec.validate
42
28
  end
43
29
 
44
- Rake::RDocTask.new do |rdoc|
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = 'test'
47
- rdoc.options << '--line-numbers' << '--inline-source'
48
- rdoc.rdoc_files.include('README*')
49
- rdoc.rdoc_files.include('lib/**/*.rb')
30
+ desc 'Run tests'
31
+ task :test do |t|
32
+ sh 'bacon -q -Ilib -I. test/*_test.rb'
50
33
  end
51
34
 
52
- task :default => :test
35
+ task :default => :test
data/gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems' unless Object.const_defined?(:Gem)
3
+ require File.dirname(__FILE__) + "/lib/boson/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "boson"
7
+ s.version = Boson::VERSION
8
+ s.authors = ["Gabriel Horner"]
9
+ s.email = "gabriel.horner@gmail.com"
10
+ s.homepage = "http://tagaholic.me/boson/"
11
+ s.summary = "A command/task framework similar to rake and thor that opens your ruby universe to the commandline and irb."
12
+ s.description = "Boson provides users with the power to turn any ruby method into a full-fledged commandline tool. Boson achieves this with powerful options (borrowed from thor) and views (thanks to hirb). Some other unique features that differentiate it from rake and thor include being accessible from irb and the commandline, being able to write boson commands in non-dsl ruby and toggling a pretty view of a command's output without additional view code."
13
+ s.required_rubygems_version = ">= 1.3.6"
14
+ s.rubyforge_project = 'tagaholic'
15
+ s.add_dependency 'hirb', '>= 0.2.10'
16
+ s.add_dependency 'alias', '>= 0.2.1'
17
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c}]) + %w{Rakefile gemspec}
18
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
19
+ end
data/lib/boson.rb CHANGED
@@ -1,11 +1,10 @@
1
- $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirname(__FILE__))
2
1
  %w{hirb alias}.each {|e| require e }
3
2
  %w{runner runners/console_runner repo manager loader inspector library}.each {|e| require "boson/#{e}" }
4
3
  %w{argument method comment}.each {|e| require "boson/inspectors/#{e}_inspector" }
5
4
  # order of library subclasses matters
6
5
  %w{module file gem require local_file}.each {|e| require "boson/libraries/#{e}_library" }
7
6
  (%w{namespace view command util commands option_parser options} +
8
- %w{index repo_index scientist option_command pipe pipes}).each {|e| require "boson/#{e}" }
7
+ %w{index repo_index scientist option_command pipe pipes version}).each {|e| require "boson/#{e}" }
9
8
 
10
9
  # This module stores the libraries, commands, repos and main object used throughout Boson.
11
10
  #
@@ -20,6 +19,7 @@ $:.unshift File.dirname(__FILE__) unless $:.include? File.expand_path(File.dirna
20
19
  module Boson
21
20
  # Module which is extended by Boson.main_object to give it command functionality.
22
21
  module Universe; include Commands::Namespace; end
22
+ NAMESPACE = '.' # Delimits namespace from command
23
23
  extend self
24
24
  # The object which holds and executes all command functionality
25
25
  attr_accessor :main_object
@@ -82,7 +82,7 @@ module Boson
82
82
 
83
83
  # Invoke command string even with namespaces
84
84
  def full_invoke(cmd, args) #:nodoc:
85
- command, subcommand = cmd.include?('.') ? cmd.split('.', 2) : [cmd, nil]
85
+ command, subcommand = cmd.include?(NAMESPACE) ? cmd.split(NAMESPACE, 2) : [cmd, nil]
86
86
  dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
87
87
  dispatcher.send(subcommand || command, *args)
88
88
  end
@@ -93,4 +93,4 @@ module Boson
93
93
  end
94
94
  end
95
95
 
96
- Boson.main_object = self
96
+ Boson.main_object = self
data/lib/boson/command.rb CHANGED
@@ -20,7 +20,7 @@ module Boson
20
20
  # Finds a command, namespaced or not and aliased or not. If found returns the
21
21
  # command object, otherwise returns nil.
22
22
  def self.find(command, commands=Boson.commands)
23
- command, subcommand = command.to_s.split('.', 2)
23
+ command, subcommand = command.to_s.split(NAMESPACE, 2)
24
24
  is_namespace_command = lambda {|current_command|
25
25
  [current_command.name, current_command.alias].include?(subcommand) &&
26
26
  current_command.library && (current_command.library.namespace == command)
@@ -133,7 +133,7 @@ module Boson
133
133
  def file_string_and_method_for_args(lib)
134
134
  if !lib.is_a?(ModuleLibrary) && (klass_method = (lib.class_commands || {})[@name])
135
135
  if RUBY_VERSION >= '1.9'
136
- klass, meth = klass_method.split('.', 2)
136
+ klass, meth = klass_method.split(NAMESPACE, 2)
137
137
  if (meth_locations = MethodInspector.find_method_locations_for_19(klass, meth))
138
138
  file_string = File.read meth_locations[0]
139
139
  end
@@ -69,7 +69,7 @@ module Boson::Commands::Core #:nodoc:
69
69
  puts "\nLOCAL OPTIONS"
70
70
  cmd.option_parser.print_usage_table options[:render_options].dup.merge(:local=>true)
71
71
  end
72
- if options[:verbose]
72
+ if options[:verbose] && cmd.render_option_parser
73
73
  puts "\nGLOBAL OPTIONS"
74
74
  cmd.render_option_parser.print_usage_table options[:render_options].dup
75
75
  end
@@ -2,18 +2,21 @@ module Boson::Commands::WebCore
2
2
  extend self
3
3
 
4
4
  def config #:nodoc:
5
- descriptions = {
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',
8
- :build_url=>"Builds a url, escaping the given params"
5
+ commands = {
6
+ 'get'=>{ :desc=>"Gets the body of a url", :args=>[['url'],['options', {}]]},
7
+ 'post'=>{ :desc=>'Posts to a url', :args=>[['url'],['options', {}]]},
8
+ 'build_url'=>{ :desc=>"Builds a url, escaping the given params", :args=>[['url'],['params']]},
9
+ 'browser'=>{ :desc=>"Opens urls in a browser on a Mac"},
10
+ 'install'=>{ :desc=>"Installs a library by url. Library should then be loaded with load_library.",
11
+ :args=>[['url'],['options', {}]],
12
+ :options=> { :name=>{:type=>:string, :desc=>"Library name to save to"},
13
+ :force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
14
+ :default=>{:type=>:boolean, :desc=>'Adds library as a default library to main config file'},
15
+ :module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
16
+ :method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
17
+ }
9
18
  }
10
- commands = descriptions.inject({}) {|h,(k,v)| h[k.to_s] = {:desc=>v}; h}
11
- commands['install'][:options] = {:name=>{:type=>:string, :desc=>"Library name to save to"},
12
- :force=>{:type=>:boolean, :desc=>'Overwrites an existing library'},
13
- :default=>{:type=>:boolean, :desc=>'Adds library as a default library to main config file'},
14
- :module_wrap=>{:type=>:boolean, :desc=>"Wraps a module around install using library name"},
15
- :method_wrap=>{:type=>:boolean, :desc=>"Wraps a method and module around installed library using library name"}}
16
- commands['install'][:args] = [['url'],['options', {}]]
19
+
17
20
  {:library_file=>File.expand_path(__FILE__), :commands=>commands, :namespace=>false}
18
21
  end
19
22
 
@@ -39,7 +42,8 @@ module Boson::Commands::WebCore
39
42
  end
40
43
 
41
44
  def_which_requires(:post, 'uri', 'net/http') do |url, options|
42
- Net::HTTP.post_form(URI.parse(url), options)
45
+ options ||= {}
46
+ (res = Net::HTTP.post_form(URI.parse(url), options)) && res.body
43
47
  end
44
48
 
45
49
  def install(url, options={}) #:nodoc:
data/lib/boson/manager.rb CHANGED
@@ -77,7 +77,7 @@ module Boson
77
77
  end
78
78
 
79
79
  def load_dependencies(lib, options={})
80
- lib_dependencies[lib] = (lib.dependencies || []).map do |e|
80
+ lib_dependencies[lib] = Array(lib.dependencies).map do |e|
81
81
  next if loaded?(e)
82
82
  load_once(e, options.merge(:dependency=>true)) ||
83
83
  raise(LoaderError, "Can't load dependency #{e}")
data/lib/boson/pipes.rb CHANGED
@@ -44,9 +44,9 @@ module Boson
44
44
  sort_lambda = lambda {}
45
45
  if object[0].is_a?(Hash)
46
46
  sort = sort.to_i if sort.to_s[/^\d+$/]
47
- sort_lambda = (object.all? {|e| e[sort].respond_to?(:<=>) } ? lambda {|e| e[sort] } : lambda {|e| e[sort].to_s })
47
+ sort_lambda = untouched_sort?(object.map {|e| e[sort] }) ? lambda {|e| e[sort] } : lambda {|e| e[sort].to_s }
48
48
  else
49
- sort_lambda = object.all? {|e| e.send(sort).respond_to?(:<=>) } ? lambda {|e| e.send(sort) || ''} :
49
+ sort_lambda = untouched_sort?(object.map {|e| e.send(sort) }) ? lambda {|e| e.send(sort) || ''} :
50
50
  lambda {|e| e.send(sort).to_s }
51
51
  end
52
52
  object.sort_by &sort_lambda
@@ -54,6 +54,10 @@ module Boson
54
54
  $stderr.puts "Sort failed with nonexistant method '#{sort}'"
55
55
  end
56
56
 
57
+ def untouched_sort?(values) #:nodoc:
58
+ values.all? {|e| e.respond_to?(:<=>) } && values.map {|e| e.class }.uniq.size == 1
59
+ end
60
+
57
61
  # Reverse an object
58
62
  def reverse_sort_pipe(object, extra=nil)
59
63
  object.reverse
@@ -99,7 +99,7 @@ module Boson
99
99
 
100
100
  def find_library(command, object=false)
101
101
  read
102
- namespace_command = command.split('.')[0]
102
+ namespace_command = command.split(NAMESPACE)[0]
103
103
  if (lib = @libraries.find {|e| e.namespace == namespace_command })
104
104
  object ? lib : lib.name
105
105
  elsif (cmd = Command.find(command, @commands))
data/lib/boson/runner.rb CHANGED
@@ -56,9 +56,9 @@ module Boson
56
56
  {:verbose=>@options[:verbose]}
57
57
  end
58
58
 
59
- def autoload_command(cmd)
59
+ def autoload_command(cmd, opts={:verbose=>verbose?})
60
60
  Index.read
61
- (lib = Index.find_library(cmd)) && Manager.load(lib, :verbose=>verbose?)
61
+ (lib = Index.find_library(cmd)) && Manager.load(lib, opts)
62
62
  lib
63
63
  end
64
64
 
@@ -34,6 +34,7 @@ module Boson
34
34
  class BinRunner < Runner
35
35
  GLOBAL_OPTIONS = {
36
36
  :verbose=>{:type=>:boolean, :desc=>"Verbose description of loading libraries, errors or help"},
37
+ :version=>{:type=>:boolean, :desc=>"Prints the current version"},
37
38
  :index=>{:type=>:array, :desc=>"Libraries to index. Libraries must be passed with '='.",
38
39
  :bool_default=>nil, :values=>all_libraries, :regexp=>true, :enum=>false},
39
40
  :execute=>{:type=>:string, :desc=>"Executes given arguments as a one line script"},
@@ -47,44 +48,61 @@ module Boson
47
48
  :option_commands=>{:type=>:boolean, :desc=>"Toggles on all commands to be defined as option commands" }
48
49
  } #:nodoc:
49
50
 
51
+ PIPE = '+'
52
+
50
53
  class <<self
51
54
  attr_accessor :command
52
55
 
53
56
  # Starts, processes and ends a commandline request.
54
57
  def start(args=ARGV)
55
58
  @command, @options, @args = parse_args(args)
59
+ return puts("boson #{Boson::VERSION}") if @options[:version]
56
60
  return print_usage if args.empty? || (@command.nil? && !@options[:console] && !@options[:execute])
57
61
  return ConsoleRunner.bin_start(@options[:console], @options[:load]) if @options[:console]
58
62
  init
59
- View.toggle_pager if @options[:pager_toggle]
60
63
 
61
64
  if @options[:help]
65
+ autoload_command @command
62
66
  Boson.invoke(:usage, @command, :verbose=>@options[:verbose])
63
67
  elsif @options[:execute]
68
+ define_autoloader
64
69
  Boson.main_object.instance_eval @options[:execute]
65
70
  else
66
71
  execute_command
67
72
  end
73
+ rescue NoMethodError
74
+ print_error_message no_method_error_message
68
75
  rescue
69
- is_invalid_command = lambda {|command| !Boson.can_invoke?(command[/\w+/]) ||
70
- (Boson.can_invoke?(command[/\w+/]) && command.include?('.') && $!.is_a?(NoMethodError)) }
71
- print_error_message @command.to_s[/\w+/] && is_invalid_command.call(@command) ?
72
- "Error: Command '#{@command}' not found" : "Error: #{$!.message}"
76
+ print_error_message default_error_message
73
77
  end
74
78
 
75
- # Loads the given command.
79
+ def no_method_error_message #:nodoc:
80
+ @command = @command.to_s
81
+ if $!.backtrace.grep(/`(invoke|full_invoke)'$/).empty? ||
82
+ !$!.message[/undefined method `(\w+\.)?#{@command.split(NAMESPACE)[-1]}'/]
83
+ default_error_message
84
+ else
85
+ @command.to_s[/\w+/] &&
86
+ (!(Index.read && Index.find_command(@command[/\w+/])) || @command.include?(NAMESPACE)) ?
87
+ "Error: Command '#{@command}' not found" : default_error_message
88
+ end
89
+ end
90
+
91
+ # Loads libraries and handles non-critical options
76
92
  def init
77
93
  Runner.in_shell = true
78
94
  Command.all_option_commands = true if @options[:option_commands]
79
95
  super
80
- Index.update(:verbose=>true, :libraries=>@options[:index]) if @options.key?(:index)
81
- if @options[:load]
82
- Manager.load @options[:load], load_options
83
- elsif @options[:execute]
84
- define_autoloader
85
- else
86
- load_command_by_index
96
+
97
+ if @options.key?(:index)
98
+ Index.update(:verbose=>true, :libraries=>@options[:index])
99
+ @index_updated = true
100
+ elsif !@options[:help] && @command && Boson.can_invoke?(@command)
101
+ Index.update(:verbose=>@options[:verbose])
102
+ @index_updated = true
87
103
  end
104
+ Manager.load @options[:load], load_options if @options[:load]
105
+ View.toggle_pager if @options[:pager_toggle]
88
106
  end
89
107
 
90
108
  # Hash of global options passed in from commandline
@@ -92,17 +110,27 @@ module Boson
92
110
  @options ||= {}
93
111
  end
94
112
 
113
+ # Commands to executed, in order given by user
114
+ def commands
115
+ @commands ||= @all_args.map {|e| e[0]}
116
+ end
95
117
  #:stopdoc:
96
118
  def print_error_message(message)
97
119
  message += "\nOriginal error: #{$!}\n" + $!.backtrace.slice(0,10).map {|e| " " + e }.join("\n") if options[:verbose]
98
120
  $stderr.puts message
99
121
  end
100
122
 
101
- def load_command_by_index
102
- Index.update(:verbose=>@options[:verbose]) if !@options.key?(:index) && Boson.can_invoke?(@command) && !@options[:help]
103
- if !Boson.can_invoke?(@command, false) && ((lib = Index.find_library(@command)) ||
104
- (Index.update(:verbose=>@options[:verbose]) && (lib = Index.find_library(@command))))
105
- Manager.load lib, load_options
123
+ def default_error_message
124
+ "Error: #{$!.message}"
125
+ end
126
+
127
+ def autoload_command(cmd)
128
+ if !Boson.can_invoke?(cmd, false)
129
+ unless @index_updated
130
+ Index.update(:verbose=>@options[:verbose])
131
+ @index_updated = true
132
+ end
133
+ super(cmd, load_options)
106
134
  end
107
135
  end
108
136
 
@@ -112,24 +140,39 @@ module Boson
112
140
  end
113
141
 
114
142
  def execute_command
115
- begin
116
- output = Boson.full_invoke(@command, @args)
117
- rescue ArgumentError
118
- raise unless $!.message[/wrong number of arguments/] &&
119
- # Throw out errors that aren't external or from option_command
120
- ($!.backtrace.first[/boson/].nil? || $!.backtrace.first[/boson\/option_command.rb/])
121
- print_error_message "'#{@command}' was called incorrectly."
122
- Boson.invoke(:usage, @command, :one_line=>true)
123
- return
124
- end
143
+ output = @all_args.inject(nil) {|acc, (cmd,*args)|
144
+ begin
145
+ @command = cmd # for external errors
146
+ autoload_command cmd
147
+ args = translate_args(args, acc)
148
+ Boson.full_invoke(cmd, args)
149
+ rescue ArgumentError
150
+ if $!.class == OptionCommand::CommandArgumentError || ($!.message[/wrong number of arguments/] &&
151
+ (cmd_obj = Command.find(cmd)) && cmd_obj.arg_size != args.size)
152
+ print_error_message "'#{cmd}' was called incorrectly."
153
+ Boson.invoke(:usage, cmd, :one_line=>true)
154
+ return
155
+ else
156
+ raise
157
+ end
158
+ end
159
+ }
125
160
  render_output output
126
161
  end
127
162
 
163
+ def translate_args(args, piped)
164
+ args.unshift piped if piped
165
+ args
166
+ end
167
+
128
168
  def parse_args(args)
169
+ @all_args = Util.split_array_by(args, PIPE)
170
+ args = @all_args[0]
129
171
  @option_parser = OptionParser.new(GLOBAL_OPTIONS)
130
172
  options = @option_parser.parse(args.dup, :opts_before_args=>true)
131
173
  new_args = @option_parser.non_opts
132
- [new_args.shift, options, new_args]
174
+ @all_args[0] = new_args
175
+ [new_args[0], options, new_args[1..-1]]
133
176
  end
134
177
 
135
178
  def render_output(output)