Module: Boson::Scientist

Extended by:
Scientist
Included in:
Scientist
Defined in:
lib/boson/scientist.rb

Overview

Scientist wraps around and redefines an object's method to give it the following features:

  • Methods can take shell command input with options or receive its normal arguments. See the Commandification section.

  • Methods have a slew of global options available. See OptionCommand for an explanation of basic global options.

The main methods Scientist provides are redefine_command() for redefining an object's method with a Command object and commandify() for redefining with a hash of method attributes. Note that for an object's method to be redefined correctly, its last argument must expect a hash.

Commandification

Take for example this basic method/command with an options definition:

options :level=>:numeric, :verbose=>:boolean
def foo(*args)
  args
end

When Scientist wraps around foo(), it can take arguments normally or as a shell command:

foo 'one', 'two', :verbose=>true   # normal call
foo 'one two -v'                 # commandline call

Both calls return: ['one', 'two', {:verbose=>true}]

Non-string arguments can be passed as well:

foo Object, 'two', :level=>1
foo Object, 'two -l1'

Both calls return: [Object, 'two', {:level=>1}]

Defined Under Namespace

Classes: Error

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#global_optionsObject

Returns the value of attribute global_options



38
39
40
# File 'lib/boson/scientist.rb', line 38

def global_options
  @global_options
end

Instance Method Details

#after_parseObject

Hook method available after parse in translate_args



119
# File 'lib/boson/scientist.rb', line 119

def after_parse; end

#analyze(obj, command, args, &block) ⇒ Object

Runs a command given its object and arguments



103
104
105
106
107
108
109
110
111
# File 'lib/boson/scientist.rb', line 103

def analyze(obj, command, args, &block)
  @global_options, @command, @original_args = {}, command, args.dup
  @args = translate_args(obj, args)
  return run_help_option(@command) if @global_options[:help]
  during_analyze(&block)
rescue OptionParser::Error, Error
  raise if Boson.in_shell
  warn "Error: #{$!}"
end

#commandify(obj, hash) ⇒ Object

A wrapper around redefine_command that doesn't depend on a Command object. Rather you simply pass a hash of command attributes (see Command.new) or command methods and let OpenStruct mock a command. The only required attribute is :name, though to get any real use you should define :options and :arg_size (default is '*'). Example:

>> def checkit(*args); args; end
=> nil
>> Boson::Scientist.commandify(self, :name=>'checkit', :options=>{:verbose=>:boolean, :num=>:numeric})
=> ['checkit']
# regular ruby method
>> checkit 'one', 'two', :num=>13, :verbose=>true
=> ["one", "two", {:num=>13, :verbose=>true}]
# commandline ruby method
>> checkit 'one two -v -n=13'
=> ["one", "two", {:num=>13, :verbose=>true}]

Raises:

  • (ArgumentError)


69
70
71
72
73
74
75
76
# File 'lib/boson/scientist.rb', line 69

def commandify(obj, hash)
  raise ArgumentError, ":name required" unless hash[:name]
  hash[:arg_size] ||= '*'
  hash[:has_splat_args?] = true if hash[:arg_size] == '*'
  fake_cmd = OpenStruct.new(hash)
  fake_cmd.option_parser ||= OptionParser.new(fake_cmd.options || {})
  redefine_command(obj, fake_cmd)
end

#during_analyze(&block) ⇒ Object

Overridable method called during analyze



114
115
116
# File 'lib/boson/scientist.rb', line 114

def during_analyze(&block)
  process_result call_original_command(@args, &block)
end

#object_methods(obj) ⇒ Object

Returns hash of methods for an object



93
94
95
# File 'lib/boson/scientist.rb', line 93

def object_methods(obj)
  @object_methods[obj] ||= {}
end

#option_command(cmd = @command) ⇒ Object

option command for given command



98
99
100
# File 'lib/boson/scientist.rb', line 98

def option_command(cmd=@command)
  @option_commands[cmd] ||= OptionCommand.new(cmd)
end

#redefine_command(obj, command) ⇒ Object

Redefines an object's method with a Command of the same name.



44
45
46
47
48
49
50
51
52
# File 'lib/boson/scientist.rb', line 44

def redefine_command(obj, command)
  cmd_block = redefine_command_block(obj, command)
  @no_option_commands << command if command.options.nil?
  [command.name, command.alias].compact.each {|e|
    obj.singleton_class.send(:define_method, e, cmd_block)
  }
rescue Error
  warn "Error: #{$!.message}"
end

#redefine_command_block(obj, command) ⇒ Object

The actual method which redefines a command's original method



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/boson/scientist.rb', line 79

def redefine_command_block(obj, command)
  object_methods(obj)[command.name] ||= begin
    obj.method(command.name)
  rescue NameError
    raise Error, "No method exists to redefine command '#{command.name}'."
  end
  lambda {|*args|
    Scientist.analyze(obj, command, args) {|args|
      Scientist.object_methods(obj)[command.name].call(*args)
    }
  }
end