Class: Cuprum::Command

Inherits:
Object
  • Object
show all
Extended by:
SleepingKingStudios::Tools::Toolbox::Subclass
Includes:
Currying, Processing, Steps
Defined in:
lib/cuprum/command.rb

Overview

Functional object that encapsulates a business logic operation or step.

Using commands allows the developer to maintain a state or context, such as by passing context into the constructor. It provides a consistent interface by always returning a Cuprum::Result object, which tracks the status of the command call, the returned value, and the error object (if any). Finally, as a full-fledged Ruby object a Command can be passed around like any other object, including returned from a method (or another Command) or passed in as a parameter.

A Command can be defined either by passing a block to the constructor, or by defining a subclass of Command and implementing the #process method.

Examples:

A Command with a block

double_command = Cuprum::Command.new { |int| 2 * int }
result         = double_command.call(5)

result.value #=> 10

A Command subclass

class MultiplyCommand < Cuprum::Command
  def initialize(multiplier)
    @multiplier = multiplier
  end

  private def process(int)
    int * @multiplier
  end
end

triple_command = MultiplyCommand.new(3)
result         = command_command.call(5)

result.value #=> 15

A Command with an error state

class DivideCommand < Cuprum::Command
  def initialize(divisor)
    @divisor = divisor
  end

  private def process(int)
    if @divisor.zero?
      return Cuprum::Result.new(error: 'errors.messages.divide_by_zero')
    end

    int / @divisor
  end
end

halve_command = DivideCommand.new(2)
result        = halve_command.call(10)

result.error #=> nil
result.value #=> 5

divide_command = DivideCommand.new(0)
result         = divide_command.call(10)

result.error #=> 'errors.messages.divide_by_zero'
result.value #=> nil

See Also:

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Steps

#step, #steps

Methods included from Currying

#curry

Methods included from Processing

#arity

Constructor Details

#initialize {|arguments, keywords, block| ... } ⇒ Command

Returns a new instance of Cuprum::Command.

Yields:

  • If a block is given, the block is used to define a private #process method. This overwrites any existing #process method. When the command is called, #process will be called internally and passed the parameters.

Yield Parameters:

  • arguments (Array)

    the arguments passed to #call.

  • keywords (Hash)

    the keywords passed to #call.

  • block (Proc, nil)

    the block passed to call, #if any.

Yield Returns:

  • (Cuprum::Result, Object)

    the returned result or object is converted to a Cuprum::Result and returned by #call.



110
111
112
113
114
115
116
117
118
# File 'lib/cuprum/command.rb', line 110

def initialize(&implementation)
  return unless implementation

  define_singleton_method :process_block, &implementation

  singleton_class.send(:private, :process_block)

  @process_block = true
end

Class Method Details

.subclass(*class_arguments, **class_keywords) { ... } ⇒ Class

Creates a subclass with partially applied constructor parameters.

Parameters:

  • class_arguments (Array)

    the arguments, if any, to apply to the constructor. These arguments will be added before any args passed directly to the constructor.

  • class_keywords (Hash)

    the keywords, if any, to apply to the constructor. These keywords will be added before any kwargs passed directly to the constructor.

Yields:

  • the block, if any, to pass to the constructor. This will be overriden by a block passed directly to the constructor.

Returns:

  • (Class)

    the generated subclass.



# File 'lib/cuprum/command.rb', line 81


Instance Method Details

#call(*arguments, **keywords) { ... } ⇒ Cuprum::Result

Executes the command and returns a Cuprum::Result or compatible object.

Each time #call is invoked, the object performs the following steps:

  1. The #process method is called, passing the arguments, keywords, and block that were passed to #call.

  2. If the value returned by #process is a Cuprum::Result or compatible object, that result is directly returned by #call.

  3. Otherwise, the value returned by #process will be wrapped in a successful result, which will be returned by #call.

Parameters:

  • arguments (Array)

    Arguments to be passed to the implementation.

  • keywords (Hash)

    Keywords to be passed to the implementation.

Yields:

  • If a block argument is given, it will be passed to the implementation.

Returns:



121
122
123
# File 'lib/cuprum/command.rb', line 121

def call(*args, **kwargs, &)
  steps { super }
end

#process(*arguments, **keywords) { ... } ⇒ Cuprum::Result, Object

Note:

This is a private method.

The implementation of the command.

Whereas the #call method provides the public interface for calling a command, the #process method defines the actual implementation. This method should not be called directly.

When the command is called via #call, the parameters are passed to #process. If #process returns a result, that result will be returned by #call; otherwise, the value returned by #process will be wrapped in a successful Cuprum::Result object.

Parameters:

  • arguments (Array)

    The arguments, if any, passed from #call.

  • keywords (Hash)

    The keywords, if any, passed from #call.

Yields:

  • The block, if any, passed from #call.

Returns:

  • (Cuprum::Result, Object)

    a result object, or the value of the result object to be returned by #call.



148
149
150
# File 'lib/cuprum/command.rb', line 148

def process(...)
  process_block? ? process_block(...) : super
end

#to_procProc

Wraps the command in a proc.

Calling the proc will call the command with the given arguments, keywords, and block.

Returns:

  • (Proc)

    the wrapping proc.



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/cuprum/command.rb', line 131

def to_proc
  command = self

  @to_proc ||= lambda do |*args, **kwargs, &block|
    if kwargs.empty?
      command.call(*args, &block)
    else
      command.call(*args, **kwargs, &block)
    end
  end
end