Class: Boson::OptionCommand

Inherits:
Object
  • Object
show all
Defined in:
lib/boson/option_command.rb

Overview

A class used by Scientist to wrap around Command objects. It’s main purpose is to parse a command’s global options (basic, render and pipe types) and local options. As the names imply, global options are available to all commands while local options are specific to a command. When passing options to commands, global ones must be passed first, then local ones. Also, options must all be passed either before or after arguments. For more about pipe and render options see Pipe and View respectively.

Basic Global Options

Any command with options comes with basic global options. For example ‘-hv’ on an option command prints a help summarizing global and local options. Another basic global option is –pretend. This option displays what global options have been parsed and the actual arguments to be passed to a command if executed. For example:

# Define this command in a library
options :level=>:numeric, :verbose=>:boolean
def foo(*args)
  args
end

irb>> foo 'testin -p -l=1'
Arguments: ["testin", {:level=>1}]
Global options: {:pretend=>true}

If a global option conflicts with a local option, the local option takes precedence. You can get around this by passing global options after a ‘-’. For example, if the global option -f (–fields) conflicts with a local -f (–force):

foo 'arg1 -v -f - -f=f1,f2'
# is the same as
foo 'arg1 -v --fields=f1,f2 -f'

Toggling Views With the Basic Global Option –render

One of the more important global options is –render. This option toggles the rendering of a command’s output done with View and Hirb.

Here’s a simple example of toggling Hirb’s table view:

# Defined in a library file:
#@options {}
def list(options={})
  [1,2,3]
end

Using it in irb:
>> list
=> [1,2,3]
>> list '-r'  # or list --render
+-------+
| value |
+-------+
| 1     |
| 2     |
| 3     |
+-------+
3 rows in set
=> true

Defined Under Namespace

Classes: CommandArgumentError

Constant Summary collapse

BASIC_OPTIONS =
{
  :help=>{:type=>:boolean, :desc=>"Display a command's help"},
  :render=>{:type=>:boolean, :desc=>"Toggle a command's default rendering behavior"},
  :verbose=>{:type=>:boolean, :desc=>"Increase verbosity for help, errors, etc."},
  :usage_options=>{:type=>:string, :desc=>"Render options to pass to usage/help"},
  :pretend=>{:type=>:boolean, :desc=>"Display what a command would execute without executing it"},
  :delete_options=>{:type=>:array, :desc=>'Deletes global options starting with given strings' }
}
RENDER_OPTIONS =

:nodoc:

{
  :fields=>{:type=>:array, :desc=>"Displays fields in the order given"},
  :class=>{:type=>:string, :desc=>"Hirb helper class which renders"},
  :max_width=>{:type=>:numeric, :desc=>"Max width of a table"},
  :vertical=>{:type=>:boolean, :desc=>"Display a vertical table"},
}
PIPE_OPTIONS =

:nodoc:

{
  :sort=>{:type=>:string, :desc=>"Sort by given field"},
  :reverse_sort=>{:type=>:boolean, :desc=>"Reverse a given sort"},
  :query=>{:type=>:hash, :desc=>"Queries fields given field:search pairs"},
  :pipes=>{:alias=>'P', :type=>:array, :desc=>"Pipe to commands sequentially"}
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cmd) ⇒ OptionCommand

Returns a new instance of OptionCommand.



106
107
108
# File 'lib/boson/option_command.rb', line 106

def initialize(cmd)
  @command = cmd
end

Instance Attribute Details

#commandObject

Returns the value of attribute command.



105
106
107
# File 'lib/boson/option_command.rb', line 105

def command
  @command
end

Class Method Details

.default_option_parserObject

:stopdoc:



86
87
88
89
# File 'lib/boson/option_command.rb', line 86

def default_option_parser
  @default_option_parser ||= OptionParser.new default_pipe_options.
    merge(default_render_options.merge(BASIC_OPTIONS))
end

.default_pipe_optionsObject



91
92
93
# File 'lib/boson/option_command.rb', line 91

def default_pipe_options
  @default_pipe_options ||= PIPE_OPTIONS.merge Pipe.pipe_options
end

.default_render_optionsObject



95
96
97
# File 'lib/boson/option_command.rb', line 95

def default_render_options
  @default_render_options ||= RENDER_OPTIONS.merge Boson.repo.config[:render_options] || {}
end

.delete_non_render_options(opt) ⇒ Object



99
100
101
# File 'lib/boson/option_command.rb', line 99

def delete_non_render_options(opt)
  opt.delete_if {|k,v| BASIC_OPTIONS.keys.include?(k) }
end

Instance Method Details

#add_default_args(args, obj) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/boson/option_command.rb', line 206

def add_default_args(args, obj)
  if @command.args && args.size < @command.args.size - 1
    # leave off last arg since its an option
    @command.args.slice(0..-2).each_with_index {|arr,i|
      next if args.size >= i + 1 # only fill in once args run out
      break if arr.size != 2 # a default arg value must exist
      begin
        args[i] = @command.file_parsed_args? ? obj.instance_eval(arr[1]) : arr[1]
      rescue Exception
        raise Scientist::Error, "Unable to set default argument at position #{i+1}.\nReason: #{$!.message}"
      end
    }
  end
end

#all_global_optionsObject



163
164
165
166
167
168
169
# File 'lib/boson/option_command.rb', line 163

def all_global_options
  OptionParser.make_mergeable! @command.render_options
  render_opts = Util.recursive_hash_merge(@command.render_options, Util.deep_copy(self.class.default_render_options))
  merged_opts = Util.recursive_hash_merge Util.deep_copy(self.class.default_pipe_options), render_opts
  opts = Util.recursive_hash_merge merged_opts, Util.deep_copy(BASIC_OPTIONS)
  set_global_option_defaults opts
end

#check_argument_size(args) ⇒ Object



198
199
200
201
202
203
204
# File 'lib/boson/option_command.rb', line 198

def check_argument_size(args)
  if args.size != @command.arg_size && !@command.has_splat_args?
    command_size, args_size = args.size > @command.arg_size ? [@command.arg_size, args.size] :
      [@command.arg_size - 1, args.size - 1]
    raise CommandArgumentError, "wrong number of arguments (#{args_size} for #{command_size})"
  end
end

#modify_args(args) ⇒ Object



191
192
193
194
195
196
# File 'lib/boson/option_command.rb', line 191

def modify_args(args)
  if @command.default_option && @command.arg_size <= 1 && !@command.has_splat_args? &&
    !args[0].is_a?(Hash) && args[0].to_s[/./] != '-' && !args.join.empty?
    args[0] = "--#{@command.default_option}=#{args[0]}"
  end
end

#option_parserObject



158
159
160
161
# File 'lib/boson/option_command.rb', line 158

def option_parser
  @option_parser ||= @command.render_options ? OptionParser.new(all_global_options) :
    self.class.default_option_parser
end

#parse(args) ⇒ Object

Parses arguments and returns global options, local options and leftover arguments.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/boson/option_command.rb', line 111

def parse(args)
  if args.size == 1 && args[0].is_a?(String)
    global_opt, parsed_options, args = parse_options Shellwords.shellwords(args[0])
  # last string argument interpreted as args + options
  elsif args.size > 1 && args[-1].is_a?(String)
    temp_args = Runner.in_shell? ? args : Shellwords.shellwords(args.pop)
    global_opt, parsed_options, new_args = parse_options temp_args
    Runner.in_shell? ? args = new_args : args += new_args
  # add default options
  elsif @command.options.nil? || @command.options.empty? || (!@command.has_splat_args? &&
    args.size <= (@command.arg_size - 1).abs) || (@command.has_splat_args? && !args[-1].is_a?(Hash))
      global_opt, parsed_options = parse_options([])[0,2]
  # merge default options with given hash of options
  elsif (@command.has_splat_args? || (args.size == @command.arg_size)) && args[-1].is_a?(Hash)
    global_opt, parsed_options = parse_options([])[0,2]
    parsed_options.merge!(args.pop)
  end
  [global_opt || {}, parsed_options, args]
end

#parse_global_options(args) ⇒ Object



154
155
156
# File 'lib/boson/option_command.rb', line 154

def parse_global_options(args)
  option_parser.parse args
end

#parse_options(args) ⇒ Object

:stopdoc:



132
133
134
135
136
137
138
139
140
141
# File 'lib/boson/option_command.rb', line 132

def parse_options(args)
  parsed_options = @command.option_parser.parse(args, :delete_invalid_opts=>true)
  trailing, unparseable = split_trailing
  global_options = parse_global_options @command.option_parser.leading_non_opts + trailing
  new_args = option_parser.non_opts.dup + unparseable
  [global_options, parsed_options, new_args]
rescue OptionParser::Error
  global_options = parse_global_options @command.option_parser.leading_non_opts + split_trailing[0]
  global_options[:help] ? [global_options, nil, []] : raise
end

#set_global_option_defaults(opts) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/boson/option_command.rb', line 171

def set_global_option_defaults(opts)
  if !opts[:fields].key?(:values)
    if opts[:fields][:default]
      opts[:fields][:values] = opts[:fields][:default]
    else
      if opts[:change_fields] && (changed = opts[:change_fields][:default])
        opts[:fields][:values] = changed.is_a?(Array) ? changed : changed.values
      end
      opts[:fields][:values] ||= opts[:headers][:default].keys if opts[:headers] && opts[:headers][:default]
    end
    opts[:fields][:enum] = false if opts[:fields][:values] && !opts[:fields].key?(:enum)
  end
  if opts[:fields][:values]
    opts[:sort][:values] ||= opts[:fields][:values]
    opts[:query][:keys] ||= opts[:fields][:values]
    opts[:query][:default_keys] ||= "*"
  end
  opts
end

#split_trailingObject



143
144
145
146
147
148
149
150
151
152
# File 'lib/boson/option_command.rb', line 143

def split_trailing
  trailing = @command.option_parser.trailing_non_opts
  if trailing[0] == '--'
    trailing.shift
    [ [], trailing ]
  else
    trailing.shift if trailing[0] == '-'
    [ trailing, [] ]
  end
end