Class: Command::BaseInterpreter

Inherits:
Object
  • Object
show all
Defined in:
lib/command-set/interpreter.rb

Overview

This is the base interpreter class. By itself it’ll raise a bunch of NoMethodErrors.

Interpreters manage the Subject object(s) and CommandSet and process input.

Subclasses of BaseInterpreter (like TextInterpreter, for instance, must implement #cook_input(raw) that converts raw input (of whatever form is appropriate to the interpreter) into CommandSetup objects. Other methods can be overridden - especially consider #get_formatter, and #prompt_user

Direct Known Subclasses

QuickInterpreter, TextInterpreter

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBaseInterpreter

Returns a new instance of BaseInterpreter.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/command-set/interpreter.rb', line 56

def initialize
  @command_set=nil
  @sub_modes = []
  @behavior = {
    :screen_width => 76,
    :warn_no_undo => true
  }
  @out_io = $stdout
  @stop = false
  @subject = nil
  @logger = Logger.new($stderr)
  @logger.level=Logger::FATAL
  @undo_stack = UndoStack.new
  @commands_pending = []
  @pause_decks = Hash.new {|h,k| h[k]=[]}
end

Instance Attribute Details

#command_setObject

Returns the value of attribute command_set.



16
17
18
# File 'lib/command-set/interpreter.rb', line 16

def command_set
  @command_set
end

#loggerObject

Returns the value of attribute logger.



16
17
18
# File 'lib/command-set/interpreter.rb', line 16

def logger
  @logger
end

#out_ioObject

Returns the value of attribute out_io.



16
17
18
# File 'lib/command-set/interpreter.rb', line 16

def out_io
  @out_io
end

#subjectObject

Returns the value of attribute subject.



17
18
19
# File 'lib/command-set/interpreter.rb', line 17

def subject
  @subject
end

Instance Method Details

#behavior(hash) ⇒ Object

Any options that the interpreter might have can be set by passing a hash to behavior to be merged with the defaults



52
53
54
# File 'lib/command-set/interpreter.rb', line 52

def behavior(hash)
  @behavior.merge!(hash)
end

#fill_subject {|template| ... } ⇒ Object

Yields:

  • (template)


44
45
46
47
48
# File 'lib/command-set/interpreter.rb', line 44

def fill_subject
  template = subject_template
  yield template
  self.subject=(template)
end

#get_formatterObject

Gets the final Results::Formatter object to output to. Override this if you won’t be reporting output to the standard output.



182
183
184
# File 'lib/command-set/interpreter.rb', line 182

def get_formatter
  return Results::Formatter.new(::Command::raw_stdout)
end

#get_subjectObject



101
102
103
# File 'lib/command-set/interpreter.rb', line 101

def get_subject
  return Subject.new
end

#next_commandObject

Gets the next command in the queue - related to DSL::Action#chain. You’ll almost never want to override this method.



119
120
121
122
123
# File 'lib/command-set/interpreter.rb', line 119

def next_command
  setup = @commands_pending.shift
  setup.args_hash = default_arg_hash.merge(setup.args_hash)
  return setup.command_instance(current_command_set, build_subject)
end

#pop_modeObject

The compliment to #push_mode. Removes the most recent command set.



87
88
89
90
# File 'lib/command-set/interpreter.rb', line 87

def pop_mode
  @sub_modes.pop
  return nil
end

#prep_subject(subject) ⇒ Object

This method sets up the fields in the subject required by the interpreter.



107
108
109
110
111
112
113
114
115
# File 'lib/command-set/interpreter.rb', line 107

def prep_subject(subject)
  @command_set.add_requirements(subject)
  subject.required_fields(*subject_requirements())
  subject.undo_stack = @undo_stack
  subject.interpreter_behavior = @behavior
  subject.chain_of_command = @commands_pending
  subject.pause_decks = @pause_decks
  return subject
end

#process_input(raw_input) ⇒ Object

Process a single unit of input from the user. Relies on cook input to convert raw_input into a CommandSetup



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/command-set/interpreter.rb', line 134

def process_input(raw_input)
  @commands_pending << cook_input(raw_input)

  presenter = Results::Presenter.new
  formatter = get_formatter
  presenter.register_formatter(formatter)
  collector = presenter.create_collector

  begin
    raise "Ack!  No OutputStandin in place!" unless $stdout.respond_to?(:add_dispatcher)
    $stdout.add_dispatcher(collector)
    until @commands_pending.empty?
      cmd = next_command
      unless cmd.class.executeable?
        raise CommandException, "incomplete command"
      end
      if ( @behavior[:warn_no_undo] and not cmd.undoable? )
        confirm = prompt_user("\"#{raw_input}\" cannot be undone.  Continue? ")
        if not ["yes", "y", "sure", "i suppose", "okay"].include? confirm.strip.downcase
          return
        end
      end
      begin
        execute(cmd, formatter, collector)
      rescue CommandException => ce
        ce.command = cmd
        raise
      rescue ResumeFrom => rf
        deck = rf.pause_deck
        @pause_decks[deck].push rf.setup
        raise unless ResumeFromOnlyThis === rf
      end
    end
  rescue ResumeFrom => rf
    @pause_decks[deck] += @commands_pending
  rescue CommandException => ce
    ce.raw_input = raw_input
    raise
  ensure
    $stdout.remove_dispatcher(collector)
  end
  presenter.done

  @commands_pending.clear
end

#prompt_user(message) ⇒ Object

Present message to the user, and get a response - usually yes or no. Non-interactive interpreters, or ones where that level of interaction is undesirable should not override this method, which returns “no”.



128
129
130
# File 'lib/command-set/interpreter.rb', line 128

def prompt_user(message)
   "no"
end

#push_mode(mode) ⇒ Object

Puts a CommandSet ahead of the current one for processing. Useful for command

modes, like Cisco's IOS with configure modes, et al.


77
78
79
80
81
82
83
84
# File 'lib/command-set/interpreter.rb', line 77

def push_mode(mode)
  unless CommandSet === mode
    raise RuntimeError, "Sub-modes must be CommandSets!" 
  end

  @sub_modes.push([mode, mode.most_recent_args])
  return nil
end

#subject_requirementsObject

If your interpreter needs extra fields in the subject, alter subject_requirements to return an array of those fields.



96
97
98
99
# File 'lib/command-set/interpreter.rb', line 96

def subject_requirements
  return [:undo_stack, :interpreter_behavior, 
    :chain_of_command, :pause_decks]
end

#subject_templateObject

Subject has an intentionally very tight interface (q.v.) the gist of which is that you can only assign to fields that commands require, and you can only access fields within a command that you declared that command required.



25
26
27
28
# File 'lib/command-set/interpreter.rb', line 25

def subject_template
  raise "CommandSet unset!" if @command_set.nil?
  return prep_subject(get_subject)
end