Module: Command::DSL::Action

Includes:
Formatting
Included in:
Command
Defined in:
lib/command-set/dsl.rb

Overview

The methods available within the DSL::CommandDefinition#action method

The trickiest thing to realize about writing Commands is that a CommandSet is an object that contains several Command subclasses; Commad::setup creates a subclass, and so CommandSet#command does too. It’s when a command is invoked that it’s actually instantiated.

Also note that you can access the arguments of a command as read-only attributes, and you can write to and read from instance variables, which will be local to the invocation of the command. This is especially useful for undo and redo.

Instance Method Summary collapse

Methods included from Formatting

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

Instance Method Details

#action_thread(&block) ⇒ Object

For big jobs - splitting them into subthreads and such. But they need to be debugged, and IIRC there’s a deadlock condition



632
633
634
635
636
637
638
639
# File 'lib/command-set/dsl.rb', line 632

def action_thread(&block)
  collector = sub_collector
  return Thread.new do
    $stdout.set_thread_collector(collector)
    block.call
    $stdout.remove_thread_collector(collector)
  end
end

#chain(*args) ⇒ Object

It frequently makes sense to offer shortcut chains to the user, or even commands that can only be run as part of another command. Calling chain with either a command class or a command path allows will cause that command to be invoked before returning control to the user.



574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
# File 'lib/command-set/dsl.rb', line 574

def chain(*args)
  anchor = CommandSet === args.first ? args.shift : self.parent
  setup = AnchoredCommandSetup.new(anchor)
  setup.arg_hash = Hash === args.last ? args.pop : {}

  if args.length == 1
    args = args[0]
    case args
    when Array
      setup.terms = args
    when String
      setup.terms = [args]
    when Symbol
      setup.terms = [args.to_s]
    when Class
      setup.command_class = args
    else
      raise CommandException, "Can't chain #{args.inspect}"
    end
  else
    if args.find{|arg| not (String === arg or Symbol === arg)}
      raise CommandException, "Can't chain #{args.inspect}"
    else
      setup.terms = args.map{|arg| arg.to_s}
    end
  end

  subject.chain_of_command.push(setup)
end

#chain_first(klass_or_path, args) ⇒ Object

Like #chain, but interjects the command being chained to the start of the queue, immediately after this command completes.



606
607
608
609
610
611
# File 'lib/command-set/dsl.rb', line 606

def chain_first(klass_or_path, args)
  setup = CommandSetup.new
  setup.command = klass_or_path
  setup.args_hash = args
  subject.chain_of_command.unshift(setup)
end

#defer(deck = nil) ⇒ Object

Stop here and return control to the user. If several commands are chained (c.f. #chain) and the pause is subsequently resumed (StandardCommands::Resume) the rest of the chain (not this command) will be dropped.

Raises:



548
549
550
# File 'lib/command-set/dsl.rb', line 548

def defer(deck = nil) 
  raise ResumeFromOnlyThis, deck
end

#dont_undoObject

Some commands sometimes cause side effects. When evaluating arguments, if you discover that undoing doesn’t make sense, and will be confusing to the user, call dont_undo, and the interpreter will ignore the call for purposes of undoing



524
525
526
527
# File 'lib/command-set/dsl.rb', line 524

def dont_undo
  @should_undo = false
  return nil
end

#fan_out(threads_at_a_time, array, &block) ⇒ Object



641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
# File 'lib/command-set/dsl.rb', line 641

def fan_out(threads_at_a_time, array, &block) 
  require 'thwait'

  array = array.to_a
  first_batch = (array[0...threads_at_a_time]||[]).map do |item|
    action_thread { block.call(item) }
  end

  rest = (array[threads_at_a_time..-1] || [])

  waiter = ThreadsWait.new(*first_batch)

  rest.each do |item|
    waiter.next_wait
    waiter.join_nowait(action_thread{block.call(item)})
  end

  waiter.join
end

#pause(deck = nil) ⇒ Object

Stop here. Return control to the user. If several commands are chained (c.f. #chain) and the pause is subsequently resumed (StandardCommands::Resume) the whole chain will be resumed.

Raises:



540
541
542
# File 'lib/command-set/dsl.rb', line 540

def pause(deck = nil)
  raise ResumeFrom, deck
end

#rootObject



617
618
619
# File 'lib/command-set/dsl.rb', line 617

def root
  return @nesting[0]
end

#subjectObject

This is how you’ll access the Command::Subject object that’s the interface of every command to the program state.



531
532
533
# File 'lib/command-set/dsl.rb', line 531

def subject
  @subject_image
end

#task(id) ⇒ Object

Allows for a command to be broken into pieces so that a resume can pick up within a command. The block will be executed normally, but if the command is resumed with a task id, all task blocks until that id will be skipped.



556
557
558
559
560
561
562
563
564
565
# File 'lib/command-set/dsl.rb', line 556

def task(id) #:yield:
  if not @resume_from.nil?
    if @resume_from == id
      @resume_from = nil
    end
    return
  end
  yield if block_given?
  @last_completed_task = id
end

#undo(box) ⇒ Object

Not normally called from within an #action block, this provides the default behavior for an undo (raise an exception)

Raises:



625
626
627
# File 'lib/command-set/dsl.rb', line 625

def undo(box)
  raise CommandException, "#{@name} cannot be undone"
end

#up(levels = 1) ⇒ Object



613
614
615
# File 'lib/command-set/dsl.rb', line 613

def up(levels = 1)
  return @nesting[-(levels+1)]
end