boson 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Boson
2
- # A library which takes a module as a library's name. Reload for this library
2
+ # This library takes a module as a library's name. Reload for this library
3
3
  # subclass is disabled.
4
4
  class ModuleLibrary < Library
5
5
  #:stopdoc:
@@ -24,9 +24,31 @@ module Boson
24
24
  attr_reader :except, :no_alias_creation, :new_module, :new_commands
25
25
  # Optional namespace name for a library. When enabled defaults to a library's name.
26
26
  attr_writer :namespace
27
+
27
28
  # Creates a library object with a hash of attributes which must include a :name attribute.
28
29
  # Each hash pair maps directly to an instance variable and value. Defaults for attributes
29
- # are read from config[:libraries][@library_name]. See Boson::Repo.config for more details.
30
+ # are read from config[:libraries][@library_name][@attribute].
31
+ #
32
+ # Attributes that can be configured:
33
+ # * *:dependencies*: An array of libraries that this library depends on. A library won't load
34
+ # unless its dependencies are loaded first.
35
+ # * *:commands*: A hash or array of commands that belong to this library. A hash configures command attributes
36
+ # for the given commands with command names pointing to their configs. See Command.new for a
37
+ # command's configurable attributes. If an array, the commands are set for the given library,
38
+ # overidding default command detection.
39
+ # Example:
40
+ # :commands=>{'commands'=>{:description=>'Lists commands', :alias=>'com'}}
41
+ # * *:class_commands*: A hash of commands to create. Hash should map command names to any string of ruby code
42
+ # that ends with a method call.
43
+ # Example:
44
+ # :class_commands=>{'spy'=>'Bond.spy', 'create'=>'Alias.manager.create'}
45
+ # * *:force*: Boolean which forces a library to ignore when a library's methods are overriding existing ones.
46
+ # Use with caution. Default is false.
47
+ # * *:object_methods*: Boolean which detects any Object/Kernel methods created when loading a library and automatically
48
+ # adds them to a library's commands. Default is true.
49
+ # * *:namespace*: Boolean or string which namespaces a library. When true, the library is automatically namespaced
50
+ # to the library's name. When a string, the library is namespaced to the string. Default is nil. To control the
51
+ # namespacing of all libraries see Boson::Repo.config.
30
52
  def initialize(hash)
31
53
  @name = set_name hash.delete(:name)
32
54
  @loaded = false
@@ -35,7 +57,6 @@ module Boson
35
57
  @commands_hash = {}
36
58
  @commands = []
37
59
  set_config (repo.config[:libraries][@name] || {}).merge(hash)
38
- @commands_hash = repo.config[:commands].merge @commands_hash
39
60
  set_command_aliases(repo.config[:command_aliases])
40
61
  @namespace = true if Boson.repo.config[:auto_namespace] && @namespace.nil? &&
41
62
  !Boson::Runner.default_libraries.include?(@module)
@@ -4,7 +4,7 @@ module Boson
4
4
 
5
5
  # This module is mixed into Library to give it load() and reload() functionality.
6
6
  # When creating your own Library subclass, you should override load_source_and_set_module and
7
- # reload_source_and_set_module. You can override other methods in this module as needed.
7
+ # reload_source_and_set_module . You can override other methods in this module as needed.
8
8
  module Loader
9
9
  # Loads a library and its dependencies and returns true if library loads correctly.
10
10
  def load
@@ -64,7 +64,7 @@ module Boson
64
64
  options[:object_methods] = @object_methods if !@object_methods.nil?
65
65
  detected = Util.detect(options, &block)
66
66
  @gems += detected[:gems] if detected[:gems]
67
- @commands += detected[:methods]
67
+ @commands += detected[:methods].map {|e| e.to_s }
68
68
  detected
69
69
  end
70
70
 
@@ -4,6 +4,7 @@ module Boson
4
4
  # Raised when a library's append_features returns false.
5
5
  class AppendFeaturesFalseError < StandardError; end
6
6
 
7
+ # Handles loading and reloading of libraries and commands.
7
8
  class Manager
8
9
  class <<self
9
10
  # Loads a library or an array of libraries with options. Manager loads the first library subclass
@@ -62,7 +63,7 @@ module Boson
62
63
  rescue Exception=>e
63
64
  FileLibrary.reset_file_cache(library.to_s)
64
65
  print_error_message "Unable to #{load_method} library #{library}. Reason: #{$!}" + "\n" +
65
- e.backtrace.slice(0,3).join("\n")
66
+ e.backtrace.slice(0,3).map {|e| " " + e }.join("\n")
66
67
  ensure
67
68
  Inspector.disable if Inspector.enabled
68
69
  end
@@ -103,7 +104,7 @@ module Boson
103
104
 
104
105
  def loader_create(source)
105
106
  lib_class = Library.handle_blocks.find {|k,v| v.call(source) } or raise(LoaderError, "Library #{source} not found.")
106
- lib_class[0].new(:name=>source, :index=>@options[:index])
107
+ lib_class[0].new(@options.merge(:name=>source))
107
108
  end
108
109
 
109
110
  def after_load
@@ -1,25 +1,29 @@
1
1
  module Boson
2
+ # Used in all things namespace.
2
3
  class Namespace
3
- def self.create_object_namespace(name, library)
4
- obj = library.namespace_object
5
- obj.instance_eval("class<<self;self;end").send(:define_method, :boson_commands) {
6
- self.class.instance_methods(false) }
7
- obj.instance_eval("class<<self;self;end").send(:define_method, :object_delegate?) { true }
8
- namespaces[name.to_s] = obj
9
- end
10
-
4
+ # Hash of created namespace names to namespace objects
11
5
  def self.namespaces
12
6
  @namespaces ||= {}
13
7
  end
14
8
 
9
+ # Creates a namespace given its name and the library it belongs to.
15
10
  def self.create(name, library)
16
- if library.object_namespace && library.module.instance_methods.include?(name)
11
+ if library.object_namespace && library.module.instance_methods.map {|e| e.to_s}.include?(name)
17
12
  library.include_in_universe
18
13
  create_object_namespace(name, library)
19
14
  else
20
15
  create_basic_namespace(name, library)
21
16
  end
22
17
  end
18
+ #:stopdoc:
19
+
20
+ def self.create_object_namespace(name, library)
21
+ obj = library.namespace_object
22
+ obj.instance_eval("class<<self;self;end").send(:define_method, :boson_commands) {
23
+ self.class.instance_methods(false) }
24
+ obj.instance_eval("class<<self;self;end").send(:define_method, :object_delegate?) { true }
25
+ namespaces[name.to_s] = obj
26
+ end
23
27
 
24
28
  def self.create_basic_namespace(name, library)
25
29
  namespaces[name.to_s] = new(name, library)
@@ -32,14 +36,15 @@ module Boson
32
36
  class <<self; self end.send :include, @library.module
33
37
  end
34
38
 
35
- def boson_commands
36
- @library.module.instance_methods
37
- end
38
-
39
39
  def object_delegate?; false; end
40
40
 
41
41
  def method_missing(method, *args, &block)
42
42
  Boson.can_invoke?(method) ? Boson.invoke(method, *args, &block) : super
43
43
  end
44
+ #:startdoc:
45
+ # List of subcommands for the namespace.
46
+ def boson_commands
47
+ @library.module.instance_methods.map {|e| e.to_s }
48
+ end
44
49
  end
45
50
  end
@@ -1,9 +1,9 @@
1
1
  module Boson
2
- # Simple Hash with indifferent access
3
- class IndifferentAccessHash < ::Hash
2
+ # Simple Hash with indifferent access. Used by OptionParser.
3
+ class IndifferentAccessHash < ::Hash #:nodoc:
4
4
  def initialize(hash)
5
5
  super()
6
- update hash.each {|k,v| hash[convert_key(k)] = hash.delete(k) }
6
+ hash.each {|k,v| self[k] = v }
7
7
  end
8
8
 
9
9
  def [](key)
@@ -24,9 +24,21 @@ module Boson
24
24
  end
25
25
  end
26
26
 
27
+ # This class provides option parsing for boolean, string, numeric and array
28
+ # values given a simple hash of options. Setting option values should be straightforward for
29
+ # *nix people. By option type:
30
+ # * *:boolean*: These don't have values i.e. '--debug'. To toogle a boolean, prepend with --no- i.e. '--no-debug'.
31
+ # Multiple booleans can be joined together i.e. '-d -f -t' == '-dft'.
32
+ # * *:string*: Separate name from value with space or '=' i.e. '--color red' or '--color=red'.
33
+ # * *:numeric*: Receives values as :string does or by appending number right after name i.e.
34
+ # '-N3' == '-N=3'.
35
+ # * *:array*: Receives values as :string does. Multiple values are split by ',' i.e.
36
+ # '--fields 1,2,3' -> ['1','2','3']. The split character can be configured as explained at
37
+ # OptionParser.new .
27
38
  # This is a modified version of Yehuda Katz's Thor::Options class which is a modified version
28
- # of Daniel Berger's Getopt::Long class, licensed under Ruby's license.
39
+ # of Daniel Berger's Getopt::Long class (licensed under Ruby's license).
29
40
  class OptionParser
41
+ # Raised for all OptionParser errors
30
42
  class Error < StandardError; end
31
43
 
32
44
  NUMERIC = /(\d*\.\d+|\d+)/
@@ -37,28 +49,54 @@ module Boson
37
49
  SHORT_NUM = /^(-[a-zA-Z])#{NUMERIC}$/i
38
50
 
39
51
  attr_reader :leading_non_opts, :trailing_non_opts, :opt_aliases
40
-
52
+
53
+ # Array of arguments left after defined options have been parsed out by parse.
41
54
  def non_opts
42
55
  leading_non_opts + trailing_non_opts
43
56
  end
44
57
 
45
- # Takes an array of options. Each array consists of up to three
46
- # elements that indicate the name and type of option. Returns a hash
47
- # containing each option name, minus the '-', as a key. The value
48
- # for each key depends on the type of option and/or the value provided
49
- # by the user.
58
+ # Takes a hash of options. Each option, a key-value pair, must provide the option's
59
+ # name and type. Names longer than one character are accessed with '--' while
60
+ # one character names are accessed with '-'. Names can be symbols, strings
61
+ # or even dasherized strings:
62
+ #
63
+ # Boson::OptionParser.new :debug=>:boolean, 'level'=>:numeric,
64
+ # '--fields'=>:array
65
+ #
66
+ # Options can have default values and implicit types simply by changing the
67
+ # option type for the default value:
68
+ #
69
+ # Boson::OptionParser.new :debug=>true, 'level'=>3.1, :fields=>%w{f1 f2}
70
+ #
71
+ # By default every option name longer than one character is given an alias,
72
+ # the first character from its name. For example, the --fields option
73
+ # has -f as its alias. You can override the default alias by providing your own
74
+ # option aliases as an array in the option's key.
75
+ #
76
+ # Boson::OptionParser.new [:debug, :damnit, :D]=>true
77
+ #
78
+ # Note that aliases are accessed the same way as option names. For the above,
79
+ # --debug, --damnit and -D all refer to the same option.
50
80
  #
51
- # The long option _must_ be provided. The short option defaults to the
52
- # first letter of the option. The default type is :boolean.
81
+ # Options can have additional attributes by passing a hash to the option value instead of
82
+ # a type or default:
83
+ #
84
+ # Boson::OptionParser.new :fields=>{:type=>:array, :values=>%w{f1 f2 f3},
85
+ # :enum=>false}
53
86
  #
54
- # Example:
87
+ # Here are the available option attributes:
55
88
  #
56
- # opts = Boson::OptionParser.new(
57
- # "--debug" => true,
58
- # ["--verbose", "-v"] => true,
59
- # ["--level", "-l"] => :numeric
60
- # ).parse(args)
89
+ # * *:type*: This or :default is required. Available types are :string, :boolean, :array, :numeric.
90
+ # * *:default*: This or :type is required. This is the default value an option has when not passed.
91
+ # * *:values*: An array of values an option can have. Available for :array and :string options. Values here
92
+ # can be aliased by typing a unique string it starts with. For example:
93
+ #
94
+ # For values foo, odd, optional: f refers to foo, o to odd and op to optional.
61
95
  #
96
+ # * *:enum*: Boolean indicating if an option enforces values in :values. Default is true. Available for
97
+ # :array and :string options.
98
+ # * *:split*: Only for :array options. A string or regular expression on which an array value splits
99
+ # to produce an array of values. Default is ','.
62
100
  def initialize(opts)
63
101
  @defaults = {}
64
102
  @opt_aliases = {}
@@ -114,6 +152,11 @@ module Boson
114
152
  }
115
153
  end
116
154
 
155
+ # Parses an array of arguments for defined options to return a hash. Once the parser
156
+ # recognizes a valid option, it continues to parse until an non option argument is detected.
157
+ # Flags that can be passed to the parser:
158
+ # * :opts_before_args: When true options must come before arguments. Default is false.
159
+ # * :delete_invalid_opts: When true deletes any invalid options left after parsing. Default is false.
117
160
  def parse(args, flags={})
118
161
  @args = args
119
162
  # start with defaults
@@ -151,6 +194,7 @@ module Boson
151
194
  hash
152
195
  end
153
196
 
197
+ # One-line option usage
154
198
  def formatted_usage
155
199
  return "" if @opt_types.empty?
156
200
  @opt_types.map do |opt, type|
@@ -173,6 +217,7 @@ module Boson
173
217
 
174
218
  alias :to_s :formatted_usage
175
219
 
220
+ # More verbose option help in the form of a table.
176
221
  def print_usage_table(render_options={})
177
222
  aliases = @opt_aliases.invert
178
223
  additional = [:desc, :values].select {|e| (@option_attributes || {}).values.any? {|f| f.key?(e) } }
@@ -1,35 +1,54 @@
1
1
  %w{yaml fileutils}.each {|e| require e }
2
2
  module Boson
3
+ # A class for repositories. A repository has a root directory with required subdirectories config/ and
4
+ # commands/ and optional subdirectory lib/. Each repository has a primary config file at config/boson.yml.
3
5
  class Repo
4
- def self.commands_dir(dir)
6
+ def self.commands_dir(dir) #:nodoc:
5
7
  File.join(dir, 'commands')
6
8
  end
7
9
 
8
10
  attr_accessor :dir, :config
11
+ # Creates a repository given a root directory.
9
12
  def initialize(dir)
10
13
  @dir = dir
11
14
  end
12
15
 
16
+ # Points to the config/ subdirectory and is automatically created when called. Used for config files.
13
17
  def config_dir
14
- @config_dir ||= FileUtils.mkdir_p File.join(dir, 'config')
18
+ @config_dir ||= FileUtils.mkdir_p("#{dir}/config") && "#{dir}/config"
15
19
  end
16
20
 
21
+ # Points to the commands/ subdirectory and is automatically created when called. Used for command libraries.
17
22
  def commands_dir
18
- @commands_dir ||= FileUtils.mkdir_p self.class.commands_dir(@dir)
23
+ @commands_dir ||= (cdir = self.class.commands_dir(@dir)) && FileUtils.mkdir_p(cdir) && cdir
19
24
  end
20
25
 
26
+ # A hash read from the YAML config file at config/boson.yml.
27
+ # {See here}[http://github.com/cldwalker/irbfiles/blob/master/boson/config/boson.yml] for an example config file.
21
28
  # ==== Valid config keys:
22
- # [:libraries] Hash of libraries mapping their name to attribute hashes.
23
- # [:commands] Hash of commands mapping their name to attribute hashes.
24
- # [:defaults] Array of libraries to load at start up.
25
- # [:add_load_path] Boolean specifying whether to add a load path pointing to the lib under boson's directory. Defaults to false if
26
- # the lib directory isn't defined in the boson directory. Default is false.
27
- # [:error_method_conflicts] Boolean specifying library loading behavior when one of its methods conflicts with existing methods in
29
+ # [:libraries] Hash of libraries mapping their name to attribute hashes. See Library.new for configurable attributes.
30
+ # Example:
31
+ # :libraries=>{'completion'=>{:namespace=>true}}
32
+ # [:command_aliases] Hash of commands names and their aliases. Since this is global it will be read by _all_ libraries.
33
+ # This is useful for quickly creating aliases without having to worry about placing them under
34
+ # the correct library config. For non-global aliasing, aliases should be placed under the :command_aliases
35
+ # key of a library entry in :libraries.
36
+ # Example:
37
+ # :command_aliases=>{'libraries'=>'lib', 'commands'=>'com'}
38
+ # [:console_defaults] Array of libraries to load at start up when used in irb. Default is to load all library files and libraries defined
39
+ # in the config.
40
+ # [:bin_defaults] Array of libraries to load at start up when used from the commandline. Default is no libraries.
41
+ # [:add_load_path] Boolean specifying whether to add a load path pointing to the lib subdirectory/. This is useful in sharing
42
+ # classes between libraries without resorting to packaging them as gems. Defaults to false if the lib
43
+ # subdirectory doesn't exist in the boson directory.
44
+ # [:error_method_conflicts] Boolean specifying library loading behavior when its methods conflicts with existing methods in
28
45
  # the global namespace. When set to false, Boson automatically puts the library in its own namespace.
29
46
  # When set to true, the library fails to load explicitly. Default is false.
47
+ # [:console] Console to load when using --console from commandline. Default is irb.
48
+ # [:auto_namespace] Boolean which automatically namespaces all user-defined libraries. Default is false.
30
49
  def config(reload=false)
31
50
  if reload || @config.nil?
32
- default = {:commands=>{}, :libraries=>{}, :command_aliases=>{}, :defaults=>[]}
51
+ default = {:libraries=>{}, :command_aliases=>{}, :console_defaults=>[]}
33
52
  @config = default.merge(YAML::load_file(config_dir + '/boson.yml')) rescue default
34
53
  end
35
54
  @config
@@ -1,37 +1,43 @@
1
1
  module Boson
2
+ # Base class for runners.
2
3
  class Runner
3
4
  class<<self
5
+ # Enables view, adds local load path and loads default_libraries
4
6
  def init
5
7
  View.enable
6
8
  add_load_path
7
9
  Manager.load default_libraries, load_options
8
10
  end
9
11
 
10
- def add_load_path
11
- Boson.repos.each {|repo|
12
- if repo.config[:add_load_path] || File.exists?(File.join(repo.dir, 'lib'))
13
- $: << File.join(repo.dir, 'lib') unless $:.include? File.expand_path(File.join(repo.dir, 'lib'))
14
- end
15
- }
16
- end
17
-
18
- def load_options
19
- {:verbose=>@options[:verbose]}
20
- end
21
-
12
+ # Libraries that come with Boson
22
13
  def default_libraries
23
14
  [Boson::Commands::Core, Boson::Commands::WebCore]
24
15
  end
25
16
 
17
+ # Libraries detected in repositories
26
18
  def detected_libraries
27
19
  Boson.repos.map {|repo| Dir[File.join(repo.commands_dir, '**/*.rb')].
28
20
  map {|e| e.gsub(/.*commands\//,'').gsub('.rb','') } }.flatten
29
21
  end
30
22
 
23
+ # Libraries specified in config files and detected_libraries
31
24
  def all_libraries
32
25
  (detected_libraries + Boson.repos.map {|e| e.config[:libraries].keys}.flatten).uniq
33
26
  end
34
27
 
28
+ #:stopdoc:
29
+ def add_load_path
30
+ Boson.repos.each {|repo|
31
+ if repo.config[:add_load_path] || File.exists?(File.join(repo.dir, 'lib'))
32
+ $: << File.join(repo.dir, 'lib') unless $:.include? File.expand_path(File.join(repo.dir, 'lib'))
33
+ end
34
+ }
35
+ end
36
+
37
+ def load_options
38
+ {:verbose=>@options[:verbose]}
39
+ end
40
+
35
41
  def define_autoloader
36
42
  class << ::Boson.main_object
37
43
  def method_missing(method, *args, &block)
@@ -45,7 +51,7 @@ module Boson
45
51
  end
46
52
  end
47
53
  end
48
-
54
+ #:startdoc:
49
55
  end
50
56
  end
51
57
  end
@@ -1,36 +1,53 @@
1
1
  module Boson
2
+ # Runs Boson from the commandline. Usage for the boson shell command looks like this:
3
+ # boson [GLOBAL OPTIONS] [COMMAND] [ARGS] [COMMAND OPTIONS]
4
+ #
5
+ # The boson executable comes with these global options:
6
+ # [:help] Gives a basic help of global options. When a command is given the help shifts to a command's help.
7
+ # [:verbose] Using this along with :help option shows more help. Also gives verbosity to other actions i.e. loading.
8
+ # [:index] Updates index. This should be called in the unusual case that Boson doesn't detect new commands
9
+ # and libraries.
10
+ # [:execute] Like ruby -e, this executes a string of ruby code. However, this has the advantage that all
11
+ # commands are available as normal methods, automatically loading as needed. This is a good
12
+ # way to call commands that take non-string arguments.
13
+ # [:console] This drops Boson into irb after having loaded default commands and any explict libraries with
14
+ # :load option. This is a good way to start irb with only certain libraries loaded.
15
+ # [:load] Explicitly loads a list of libraries separated by commas. Most useful when used with :console option.
16
+ # Can also be used to explicitly load libraries that aren't being detected automatically.
17
+ # [:render] Pretty formats the results of commands without options. Handy for commands that return arrays.
2
18
  class BinRunner < Runner
3
19
  GLOBAL_OPTIONS = {
4
20
  :verbose=>{:type=>:boolean, :desc=>"Verbose description of loading libraries or help"},
5
- :index=>{:type=>:boolean, :desc=>"Updates index"},
21
+ :index=>{:type=>:boolean, :desc=>"Updates index for libraries and commands"},
6
22
  :execute=>{:type=>:string, :desc=>"Executes given arguments as a one line script"},
7
- :repl=>{:type=>:boolean, :desc=>"Drops into irb or another given repl/shell with default and explicit libraries loaded"},
23
+ :console=>{:type=>:boolean, :desc=>"Drops into irb with default and explicit libraries loaded"},
8
24
  :help=>{:type=>:boolean, :desc=>"Displays this help message or a command's help if given a command"},
9
- :load=>{:type=>:array, :values=>all_libraries, :enum=>false, :desc=>"A comma delimited array of libraries to load"}
10
- }
25
+ :load=>{:type=>:array, :values=>all_libraries, :enum=>false, :desc=>"A comma delimited array of libraries to load"},
26
+ :render=>{:type=>:boolean, :desc=>"Renders a Hirb view from result of command without options"}
27
+ } #:nodoc:
11
28
 
12
29
  class <<self
13
30
  attr_accessor :command
31
+ # Starts, processes and ends a commandline request.
14
32
  def start(args=ARGV)
15
33
  @command, @options, @args = parse_args(args)
16
- return print_usage if args.empty? || (@command.nil? && !@options[:repl] && !@options[:execute])
17
- return ReplRunner.bin_start(@options[:repl], @options[:load]) if @options[:repl]
34
+ return print_usage if args.empty? || (@command.nil? && !@options[:console] && !@options[:execute])
35
+ return ConsoleRunner.bin_start(@options[:console], @options[:load]) if @options[:console]
18
36
  init
19
37
 
20
38
  if @options[:help]
21
- print_command_help
39
+ Boson.invoke(:usage, @command, :verbose=>@options[:verbose])
22
40
  elsif @options[:execute]
23
41
  Boson.main_object.instance_eval @options[:execute]
24
42
  else
25
43
  execute_command
26
44
  end
27
45
  rescue Exception
28
- message = (@command && !Boson.can_invoke?(@command)) ?
46
+ print_error_message (@command && !Boson.can_invoke?(@command[/\w+/])) ?
29
47
  "Error: Command '#{@command}' not found" : "Error: #{$!.message}"
30
- message += "\nActual error: #{$!}\n" + $!.backtrace.inspect if @options && @options[:verbose]
31
- $stderr.puts message
32
48
  end
33
49
 
50
+ # Loads the given command.
34
51
  def init
35
52
  super
36
53
  Index.update(:verbose=>true) if @options[:index]
@@ -43,6 +60,12 @@ module Boson
43
60
  end
44
61
  end
45
62
 
63
+ #:stopdoc:
64
+ def print_error_message(message)
65
+ message += "\nActual error: #{$!}\n" + $!.backtrace.inspect if @options && @options[:verbose]
66
+ $stderr.puts message
67
+ end
68
+
46
69
  def load_command_by_index
47
70
  Index.update(:verbose=>@options[:verbose]) if !@options[:index] && Boson.can_invoke?(@command) && !@options[:help]
48
71
  if !Boson.can_invoke?(@command) && ((lib = Index.find_library(@command)) ||
@@ -58,15 +81,12 @@ module Boson
58
81
  def execute_command
59
82
  command, subcommand = @command.include?('.') ? @command.split('.', 2) : [@command, nil]
60
83
  dispatcher = subcommand ? Boson.invoke(command) : Boson.main_object
61
- @args = @args.join(" ") if ((com = Boson::Command.find(@command)) && com.option_command?)
62
84
  render_output dispatcher.send(subcommand || command, *@args)
63
85
  rescue ArgumentError
64
- puts "Wrong number of arguments for #{@command}\n\n"
65
- print_command_help
66
- end
67
-
68
- def print_command_help
69
- Boson.invoke(:usage, @command, :verbose=>@options[:verbose])
86
+ # for the rare case it's raise outside of boson
87
+ raise unless $!.backtrace.first.include?('boson/')
88
+ print_error_message "'#{@command}' was called incorrectly."
89
+ Boson.invoke(:usage, @command)
70
90
  end
71
91
 
72
92
  def parse_args(args)
@@ -77,10 +97,10 @@ module Boson
77
97
  end
78
98
 
79
99
  def render_output(output)
80
- if Scientist.global_options
81
- puts output.inspect unless Scientist.rendered
82
- else
83
- View.render(output)
100
+ if Scientist.global_options && !Scientist.rendered && !View.silent_object?(output)
101
+ puts output.inspect
102
+ elsif !Scientist.global_options && @options[:render]
103
+ View.render(output, :silence_booleans=>true)
84
104
  end
85
105
  end
86
106
 
@@ -95,6 +115,7 @@ module Boson
95
115
  Boson.invoke :commands, "", :fields=>["name", "usage", "description"], :description=>false
96
116
  end
97
117
  end
118
+ #:startdoc:
98
119
  end
99
120
  end
100
121
  end