Class: Cuprum::Function

Inherits:
Object
  • Object
show all
Defined in:
lib/cuprum/function.rb

Overview

Functional object that encapsulates a business logic operation with a consistent interface and tracking of result value and status.

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

Examples:

A Function with a block

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

result.value #=> 10

A Function subclass

class MultiplyFunction < Cuprum::Function
  def initialize multiplier
    @multiplier = multiplier
  end # constructor

  private

  def process int
    int * @multiplier
  end # method process
end # class

triple_function = MultiplyFunction.new(3)
result          = triple_function.call(5)

result.value #=> 15

A Function with errors

class DivideFunction < Cuprum::Function
  def initialize divisor
    @divisor = divisor
  end # constructor

  private

  def process int
    if @divisor.zero?
      errors << 'errors.messages.divide_by_zero'

      return
    end # if

    int / @divisor
  end # method process
end # class

halve_function = DivideFunction.new(2)
result         = halve_function.call(10)

result.errors #=> []
result.value  #=> 5

function_with_errors = DivideFunction.new(0)
result               = function_with_errors.call(10)

result.errors #=> ['errors.messages.divide_by_zero']
result.value  #=> nil

Function Chaining

class AddFunction < Cuprum::Function
  def initialize addend
    @addend = addend
  end # constructor

  private

  def process int
    int + @addend
  end # method process
end # class

double_and_add_one = MultiplyFunction.new(2).chain(AddFunction.new(1))
result             = double_and_add_one(5)

result.value #=> 5

Conditional Chaining With #then And #else

class EvenFunction < Cuprum::Function
  private

  def process int
    errors << 'errors.messages.not_even' unless int.even?

    int
  end # method process
end # class

# The next step in a Collatz sequence is determined as follows:
# - If the number is even, divide it by 2.
# - If the number is odd, multiply it by 3 and add 1.
collatz_function =
  EvenFunction.new.
    then(DivideFunction.new(2)).
    else(MultiplyFunction.new(3).chain(AddFunction.new(1)))

result = collatz_function.new(5)
result.value #=> 16

result = collatz_function.new(16)
result.value #=> 8

Direct Known Subclasses

Operation

Defined Under Namespace

Classes: NotImplementedError

Instance Method Summary collapse

Constructor Details

#initialize {|*arguments, **keywords, &block| ... } ⇒ Function

Returns a new instance of Cuprum::Function.

Yields:

  • (*arguments, **keywords, &block)

    If a block is given, the #call method will wrap the block and set the result #value to the return value of the block. This overrides the implementation in #process, if any.



125
126
127
# File 'lib/cuprum/function.rb', line 125

def initialize &implementation
  define_singleton_method :process, &implementation if implementation
end

Instance Method Details

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

Executes the logic encoded in the constructor block, or the #process method if no block was passed to the constructor.

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:

Raises:

  • (NotImplementedError)

    Unless a block was passed to the constructor or the #process method was overriden by a Function subclass.



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/cuprum/function.rb', line 145

def call *args, &block
  call_chained_functions do
    Cuprum::Result.new.tap do |result|
      @errors = result.errors

      result.value = process(*args, &block)

      @errors = nil
    end # tap
  end # call_chained_functions
end

#chain(function, on: nil) ⇒ Cuprum::Function #chain(on: :nil) {|result| ... } ⇒ Cuprum::Function

Registers a function or block to run after the current function, or after the last chained function if the current function already has one or more chained function(s). This creates and modifies a copy of the current function.

Overloads:

  • #chain(function, on: nil) ⇒ Cuprum::Function

    The function will be passed the #value of the previous function result as its parameter, and the result of the chained function will be returned (or passed to the next chained function, if any).

    Parameters:

    • function (Cuprum::Function)

      The function to call after the current or last chained function.

  • #chain(on: :nil) {|result| ... } ⇒ Cuprum::Function

    The block will be passed the #result of the previous function as its parameter. If your use case depends on the status of the previous function or on any errors generated, use the block form of #chain.

    If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained function, if any). If the block returns any other value (including nil), the #result of the previous function will be returned or passed to the next function.

    Yield Parameters:

Parameters:

  • on (Symbol) (defaults to: nil)

    Sets a condition on when the chained function can run, based on the status of the previous function. Valid values are :success and :failure, and will constrain the function to run only if the previous function succeeded or failed, respectively. If no value is given, the function will run whether the previous function was a success or a failure.

Returns:



192
193
194
195
196
# File 'lib/cuprum/function.rb', line 192

def chain function = nil, on: nil, &block
  proc = convert_function_or_proc_to_proc(block || function)

  chain_function(proc, :on => on)
end

#else(function) ⇒ Cuprum::Function #else {|result| ... } ⇒ Cuprum::Function

Shorthand for function.chain(:on => :failure). Registers a function or block to run after the current function. The chained function will only run if the previous function was unsuccessfully run.

Overloads:

Returns:

See Also:



215
216
217
218
219
# File 'lib/cuprum/function.rb', line 215

def else function = nil, &block
  proc = convert_function_or_proc_to_proc(block || function)

  chain_function(proc, :on => :failure)
end

#then(function) ⇒ Cuprum::Function #then {|result| ... } ⇒ Cuprum::Function

Shorthand for function.chain(:on => :success). Registers a function or block to run after the current function. The chained function will only run if the previous function was successfully run.

Overloads:

Returns:

See Also:



238
239
240
241
242
# File 'lib/cuprum/function.rb', line 238

def then function = nil, &block
  proc = convert_function_or_proc_to_proc(block || function)

  chain_function(proc, :on => :success)
end