Class: CSD::OptionsParser

Inherits:
OpenStruct
  • Object
show all
Defined in:
lib/csd/options_parser.rb

Overview

A class that handles the command line option parsing and manipulation

Instance Method Summary (collapse)

Instance Method Details

- (Object) clear(additional_options = '')

This method resets all Options to default.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/csd/options_parser.rb', line 61

def clear(additional_options='')
  # Resetting all attributes to nil (because e.g. an application instance might have modified or added some).
  super()
  # First we define all valid actions and scopes
  self.actions       = []
  self.actions_names = []
  self.scopes        = []
  self.scopes_names  = []
  # Then we define the default literals
  self.help        = false
  self.application = nil
  self.action      = nil
  # Now we define the default options
  self.yes       = false
  self.local     = false
  self.reveal    = false
  self.verbose   = false
  self.silent    = false
  self.developer = false
  # Shortcut to allow for debugging the options parser itself
  self.debug     = (ARGV.include?('--debug') or ARGV.include?('-d'))
  # For our test suite we might want to inject more options here
  eval additional_options
end

- (Object) define_actions_and_scopes

This method assumes that the application has been chosen and tries to determine which actions and scopes the chosen application can respond to.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/csd/options_parser.rb', line 42

def define_actions_and_scopes
  if Applications.current
    # Here we overwrite the default supported actions and scopes with the application specific ones
    UI.debug "#{self.class}#define_actions_and_scopes loads the actions of #{Applications.current} now"
    self.actions       = Applications.current.actions
    public_actions    = actions['public'] ? self.actions['public'].map { |pair| pair.keys.first } : []
    developer_actions = actions['developer'] ? self.actions['developer'].map { |pair| pair.keys.first } : []
    self.actions_names = public_actions + developer_actions
    UI.debug "#{self.class}#define_actions_and_scopes identified the actions #{self.actions_names.inspect}"
    # At this point we know that the first argument is no option, but *some* action (may it be valid or not)
    UI.debug "#{self.class}#define_actions_and_scopes loads the scopes of #{Applications.current} now"
    self.scopes       = Applications.current.scopes(self.action)
    self.scopes_names = self.scopes.map { |pair| pair.keys.first }
    UI.debug "#{self.class}#define_actions_and_scopes identified the scopes #{self.scopes_names.inspect}"
  end
end

- (Object) parse!

When this method is called, the OptionsParser is populated with information gained via the CLI arguments. For example, the application, action and scope are determined and all --options are identified. Each time this method is called, all current options get lost and are replaced by what could be found in ARGV. Note that it is a destructive method, that is, the ARGV is stripped of all processed arguments. Thus this method can only be called once (that is, unless ARGV is re-populated).



18
19
20
21
22
23
# File 'lib/csd/options_parser.rb', line 18

def parse!
  clear
  parse_literals
  define_actions_and_scopes
  parse_options
end

- (Object) parse_literals

Here we check for literals, i.e. "help", ACTION and APPLICATION.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/csd/options_parser.rb', line 88

def parse_literals
  # First let's see whether we are in help mode, i.e. whether the first argument is `help´.
  # If so, we would like to remove it from the ARGV list.
  if ARGV.first == 'help'
    self.help = true
    ARGV.shift
  end
  # The action and the application name are the other literals we're interested in at this point.
  # Note: If there is no application specified, there is pretty much nothing we can do for the user.
  if Applications.current and Applications.current.name == ARGV.first
    # The application name is the first argument (i.e. there is no action specified at all)
    # Let's store the application name and remove it from the argument line
    self.application = ARGV.shift
  elsif Applications.current and Applications.current.name == ARGV.second
    # The second argument is the application name. This means that the first argument must be the
    # action or happens to be some option. In case it's no option, lets take it as desired action.
    unless ARGV.first.starts_with?('-')
      self.action      = ARGV.shift
      self.application = ARGV.shift # Removing the application name from the argument list
    end
    # The only thing that is left is the scope, by now shifted from the third to the first location.
    # A literal must now be the desired scope
    if ARGV.first and not ARGV.first.starts_with?('-')
      self.scope = ARGV.shift
    end
  end
  UI.debug "#{self.class}#parse_literals identified the application `#{self.application}´, the action `#{self.action}´ and the scope `#{self.scope}´"
end

- (Object) parse_options

Parse all options that the user gave as command parameter. Note that this function strips the options from ARGV and leaves only literal (non-option) parameters (i.e. actions/applications/scopes; strings without -- and -).



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/csd/options_parser.rb', line 120

def parse_options
  OptionParser.new do |opts|
    self.banner = "Usage: ".bold + "#{CSD.executable} [help] #{Options.action} #{Applications.current.name if Applications.current} [COMPONENT] [OPTIONS]".cyan
    opts.banner = self.banner.magenta.bold
    
    unless Options.scopes_names.empty?
      opts.headline "COMPONENTS".green.bold
      scopes.flatten.each { |scope| opts.list_item(scope.keys.first, scope.values.first) }
    end

    # Here we load application-specific options file.
    # TODO: There must be a better way for this in general than to eval the raw ruby code
    begin
      unless Applications.current.options(self.action).size == 0
        opts.headline "#{self.action.to_s.upcase} #{Applications.current.name.upcase} OPTIONS".green.bold
        eval Applications.current.options(self.action)
      else
        UI.debug "There were no options to be loaded from #{Applications.current}" 
      end
    rescue SyntaxError => e
      raise Error::Application::OptionsSyntax, "The individual options of #{Applications.current.inspect} could not be parsed (SyntaxError)."
    end if Applications.current
    
    # And here we load all general options
    options_prepend = Applications.current ? 'GENERAL ' : nil
    opts.headline "#{options_prepend}OPTIONS".green.bold
    opts.on("-y", "--yes", "Answer all questions with `yes´ (batch mode)") do |value|
      self.yes = value
    end
    #opts.on("-l", "--local","Assume that there is no uplink to the Internet") do |value|
    #  self.online = !value
    #end
    opts.on("-r", "--reveal","List all commands that normally would be executed in this operation (preview-mode)") do |value|
      self.reveal = value
    end
    opts.on("-e", "--verbose","Show more elaborate output") do |value|
      self.verbose = value
    end
    opts.on("-d", "--debug","Show debugging information about the AI") do |value|
      self.debug = value
    end
    opts.on("-s", "--silent","Don't show any output") do |value|
      self.silent = value
    end
    opts.on_tail("-a", "--developer", "Show information only relevant to software developers") do |value|
      self.developer = value
    end
    opts.on_tail("-h", "--help", "Show detailed help (regarding the given TASK and APPLICATION)") do |value|
      self.help = value
    end
    opts.on_tail("-v", "--version", "Show the version of this AI") do
      puts "CSD Gem Version: #{CSD::Version}".blue
      raise Error::Argument::VersionWasRequested
    end
    self.helptext = opts.help
  end.parse!
  rescue OptionParser::InvalidOption => e
    raise Error::Argument::InvalidOption, e.message.gsub('invalid option: ', 'This option argument seems to be incorrect: ')
end

- (Boolean) valid_action?

Determines whether the action chosen by the user is valid for the application which was chosen. Returns true if it is a valid action. Returns false if not, or if no application was chosen.

Returns:

  • (Boolean)


28
29
30
# File 'lib/csd/options_parser.rb', line 28

def valid_action?
  self.actions_names and self.actions_names.include?(self.action)
end

- (Boolean) valid_scope?

Determines whether the scope chosen by the user is valid for the application and action which was chosen. Returns true if it is a valid action. Returns false if not, or if no application was chosen.

Returns:

  • (Boolean)


35
36
37
# File 'lib/csd/options_parser.rb', line 35

def valid_scope?
  self.scopes_names and self.scopes_names.include?(self.scope)
end