Class: Command::Command

Inherits:
Object
  • Object
show all
Extended by:
CommandClassMethods, Common, DSL::CommandDefinition
Includes:
DSL::Action
Defined in:
lib/command-set/command.rb

Defined Under Namespace

Classes: DontResume

Class Attribute Summary collapse

Instance Attribute Summary collapse

Attributes included from CommandClassMethods

#advice_block, #argument_list, #context

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DSL::CommandDefinition

action, document, doesnt_undo, format_advice, parent_argument, parent_arguments, subject_methods, undo

Methods included from DSL::Argument

#alternating_argument, #argument, argument_typemap, #create, #create_decorator, document, #named_optionals, register_argument, register_decorator, #special_argument, #subject

Methods included from CommandClassMethods

consume_terms, embed_argument, executeable?, optional_argument

Methods included from Common

add_requirements, completion_list, find_command, process_terms

Methods included from DSL::Action

#action_thread, #chain, #chain_first, #defer, #dont_undo, #fan_out, #pause, #root, #subject, #task, #undo, #up

Methods included from DSL::Formatting

#begin_list, #end_list, #item, #list, #sub_collector

Constructor Details

#initialize(execution_context, resume = nil) ⇒ Command

Returns a new instance of Command.

Raises:



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/command-set/command.rb', line 256

def initialize(execution_context, resume=nil)
  raise CommandException, "#{@name}: unrecognized command" unless self.class.defined?
  @path = execution_context.command_path
  @nesting = execution_context.set_nesting

  @argument_list = self.class.argument_list.dup
  @subject_requirements = self.class.subject_requirements.dup
  resolve_parent_arguments

  subject = execution_context.subject
  context = execution_context.subject_context
  @subject_image = subject.get_image(subject_requirements || [], context)

  @arg_hash = {}
  @should_undo = true
  @validation_problem = CommandException.new("No arguments provided!")
  @last_completed_task = DontResume
  @resume_from = resume
  @main_collector = nil
end

Class Attribute Details

.definedObject (readonly)

Returns the value of attribute defined.



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

def defined
  @defined
end

.doc_textObject (readonly)

Returns the value of attribute doc_text.



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

def doc_text
  @doc_text
end

.nameObject (readonly)

Returns the value of attribute name.



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

def name
  @name
end

Instance Attribute Details

#arg_hashObject (readonly)

Returns the value of attribute arg_hash.



320
321
322
# File 'lib/command-set/command.rb', line 320

def arg_hash
  @arg_hash
end

#nestingObject (readonly)

Returns the value of attribute nesting.



320
321
322
# File 'lib/command-set/command.rb', line 320

def nesting
  @nesting
end

#pathObject (readonly)

Returns the value of attribute path.



320
321
322
# File 'lib/command-set/command.rb', line 320

def path
  @path
end

Class Method Details

.create_argument_methodsObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/command-set/command.rb', line 225

def create_argument_methods
  names = argument_list.inject([]) do |list, arg|
    list + [*arg.names]
  end

  names.each do |name|
    define_method(name) do
      @arg_hash[name]
    end
    private(name)

    define_method(name.to_s + "=") do |value|
      @arg_hash[name] = value
    end
    private(name.to_s + "=")
  end
end

.defined?Boolean

Returns:

  • (Boolean)


192
193
194
# File 'lib/command-set/command.rb', line 192

def defined?
  return @defined
end

.documentation(width, prefix = []) ⇒ Object



202
203
204
205
206
207
208
209
# File 'lib/command-set/command.rb', line 202

def documentation(width, prefix=[])
  if @doc_text.nil?
    return short_docs(width, prefix)
  else
    return short_docs(width) + 
      ["\n"] + @doc_text.wrap(width)
  end
end

.each_command(path, visitor) ⇒ Object



163
164
165
# File 'lib/command-set/command.rb', line 163

def each_command(path, visitor)
  visitor.arrive_at(path, self)
end

.inherited(subclass) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/command-set/command.rb', line 122

def inherited(subclass)
  [ [:name, proc {name.dup}],
    [:argument_list, proc {argument_list.dup }],
    [:parent_arguments, proc {parent_arguments.dup }],
    [:doc_text, proc {doc_text.nil? ? nil : doc_text.dup}], 
    [:subject_requirements, proc {subject_requirements.dup}],
    [:defined, proc {false}],
    [:advice_block, proc {advice_block.dup}],
    [:context, proc {[]}]
  ].each do |var, prok|
    subclass.instance_variable_set("@#{var.to_s}", prok.call)
  end
end

.initialize_copy(original) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/command-set/command.rb', line 136

def initialize_copy(original)
  super
  @argument_list = original.argument_list.dup
  @parent_arguments = original.parent_arguments.dup
  @subject_requirements = original.subject_requirements.dup
  @context = []
end

.inspectObject



196
197
198
199
200
# File 'lib/command-set/command.rb', line 196

def inspect
  arguments = argument_list.map{|arg| arg.name}

  return "#<Class:#{"%0#x" % self.object_id} - Command:#{name()}(#{arguments.join(", ")})>"
end

.parent_argument_listObject



167
168
169
# File 'lib/command-set/command.rb', line 167

def parent_argument_list
  @parent_arguments
end

.push_context(part) ⇒ Object



243
244
245
246
# File 'lib/command-set/command.rb', line 243

def push_context(part)
  return unless Symbol === part
  @context.unshift(part)
end

.setup(new_name = nil, &block) ⇒ Object

Establishes a subclass of Command. This is important because commands are actually classes in CommandSet; their instances are specific executions of the command, which allows for undo’s, and history management. The block will get run in the context of the new class, allowing you to quickly define the class completely.

For examples, see Command::StandardCommands



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/command-set/command.rb', line 151

def setup(new_name=nil, &block)
  command_class = Class.new(self)
  new_name = new_name.to_s

  command_class.instance_variable_set("@name", new_name)

  command_class.instance_eval &block

  command_class.defined
  return command_class
end

.short_docs(width, prefix = []) ⇒ Object



211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/command-set/command.rb', line 211

def short_docs(width, prefix=[])
  docs = prefix + [name]
  docs += argument_list.map do |arg|
    if arg.required?
      "<#{arg.name}>"
    else
      "[#{arg.name}]"
    end
  end
  

  return docs.join(" ").wrap(width)
end

.single_consume(argument, arg_hash, subject, terms) ⇒ Object



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

def single_consume(argument, arg_hash, subject, terms)
  arg_hash.merge! argument.consume(subject, terms)
end

.visit(terms, visitor) ⇒ Object Also known as: root_visit



171
172
173
174
175
176
177
178
179
# File 'lib/command-set/command.rb', line 171

def visit(terms, visitor)
  visitor.arrive_at(terms, self)

  if(terms.empty?)
    return visitor.command_out_of_terms(self)
  else
    return visitor.extra_terms(terms, self)
  end
end

Instance Method Details

#advise_formatter(formatter) ⇒ Object



371
372
373
# File 'lib/command-set/command.rb', line 371

def advise_formatter(formatter)
  formatter.receive_advice(&self.class.advice_block)
end

#all_argumentsObject



332
333
334
# File 'lib/command-set/command.rb', line 332

def all_arguments
  @argument_list
end

#consume_hash(args_hash) ⇒ Object



388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/command-set/command.rb', line 388

def consume_hash(args_hash)
  allowed_arguments = all_arguments.inject([]) do |allowed, argument|
    allowed += [*argument.name]
  end

  wrong_values = {}
  required_names = []

  args_hash.keys.each do |name|
    args_hash[name.to_s] = args_hash[name]
  end

  all_arguments.each do |argument|
    begin
      @arg_hash.merge! argument.consume_hash(@subject_image, args_hash)
    rescue ArgumentInvalidException => aie
      wrong_values.merge! aie.pairs
    rescue OutOfArgumentsException
      required_names += ([*argument.name].find_all {|name| not @arg_hash.has_key?(name)})
    end
  end

  wrong_names = @arg_hash.keys - allowed_arguments

  unless wrong_values.empty?
    aie = ArgumentInvalidException.new(wrong_values)
    @validation_problem = aie
    raise aie
  end

  unless wrong_names.empty?
    aue = ArgumentUnrecognizedException.new("Unrecognized arguments: #{wrong_names.join(", ")}")
    @validation_problem = aue
    raise aue
  end

  unless required_names.empty?
    ooae = OutOfArgumentsException.new("Missing arguments: #{required_names.join(", ")}")
    @validation_problem = ooae
    raise ooae
  end

  @validation_problem = nil
end

#go(collector) ⇒ Object



349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/command-set/command.rb', line 349

def go(collector)
  return if @resume_from == DontResume
  unless self.respond_to? :execute
    raise CommandException, "#{self.path}: command declared but no action defined" 
  end

  @main_collector = collector
  begin
    validate_arguments
    verify_image
    execute
    join_undo
  rescue Interrupt
    puts "Command cancelled"
  rescue ResumeFrom => rf
    rf.setup.task_id = @last_completed_task
    rf.setup.command_class = self.class
    rf.setup.arg_hash = arg_hash.dup
    raise rf
  end
end

#inspectObject



340
341
342
343
# File 'lib/command-set/command.rb', line 340

def inspect
  name = self.class.name
  return "#<Command:#{name}>:#{"%#x" % self.object_id} #{@arg_hash.inspect}"
end

#join_undoObject



437
438
439
440
441
442
# File 'lib/command-set/command.rb', line 437

def join_undo
  if(undoable? && @should_undo && subject.undo_stack != nil) 
    subject.undo_stack.add(self)
  end
  return nil
end

#nameObject



322
323
324
# File 'lib/command-set/command.rb', line 322

def name
  self.class.name
end

#parentObject



345
346
347
# File 'lib/command-set/command.rb', line 345

def parent
  @nesting.last
end

#required_argumentsObject



326
327
328
329
330
# File 'lib/command-set/command.rb', line 326

def required_arguments
  @argument_list.find_all do |arg|
    arg.required?
  end
end

#resolve_parent_argumentsObject



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/command-set/command.rb', line 277

def resolve_parent_arguments
  missing = []
  names = []
  self.class.parent_argument_list.uniq.each do |name|
    found = nil
    @nesting.reverse.each do |set_nesting|
      found = set_nesting.argument_list.find do |argument|
        argument.names.include? name
      end

      unless found.nil?
        @subject_requirements += found.subject_requirements
        @argument_list << found
        break
      end
    end
    if found.nil?
      missing << name
    else
      names += [*found.names]
    end
  end

  unless missing.empty?
    raise CommandError, 
      "No parent has an argument named \"#{missing.join(", ")}\"" 
  end

  names.each do |name|
    (class << self; self; end).instance_eval do
      define_method(name) do
        @arg_hash[name]
      end
      private(name)

      define_method(name.to_s + "=") do |value|
        @arg_hash[name] = value
      end
      private(name.to_s + "=")
    end
  end
end

#subject_requirementsObject



336
337
338
# File 'lib/command-set/command.rb', line 336

def subject_requirements
  @subject_requirements
end

#undoable?Boolean

Returns:

  • (Boolean)


433
434
435
# File 'lib/command-set/command.rb', line 433

def undoable?
  return false
end

#validate_argumentsObject

Raises:

  • (@validation_problem)


444
445
446
447
448
449
# File 'lib/command-set/command.rb', line 444

def validate_arguments
  raise @validation_problem if Exception === @validation_problem
  required_arguments.each do |argument|
    argument.check_present(@arg_hash.keys)
  end
end

#verify_imageObject



375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/command-set/command.rb', line 375

def verify_image
  return if subject_requirements.nil?
  subject_requirements.each do |requirement|
    begin
      if Subject::UndefinedField === @subject_image.send(requirement)
        raise CommandException, "\"#{name}\" requires \"#{requirement.to_s}\" to be set"
      end
    rescue NameError => ne
      raise CommandException, "\"#{name}\" requires subject to include \"#{requirement.to_s}\""
    end
  end
end