module Remap::State::Extension

@api private

Public Instance Methods

_(&block) click to toggle source

Validates {self} against {Schema}

Only used during development

@yield [Hash] if schema fails

@raise if schema fails and no block is given

@return [self]

# File lib/remap/state/extension.rb, line 85
def _(&block)
  return self unless mapper.validate?

  unless block
    return _ do |reason|
      raise ArgumentError, "[BUG] State: #{formatted} reason: #{reason.formatted}"
    end
  end

  unless (result = Schema.call(self)).success?
    return block[result.errors.to_h]
  end

  self
end
bind(**options, &block) click to toggle source

Passes {#value} to block, if defined {options} are combine into the final state

@yieldparam value [T] @yieldparam self [State] @yieldparam error [Proc]

@yieldreturn [Y]

@return [Y]

# File lib/remap/state/extension.rb, line 274
def bind(**options, &block)
  unless block
    raise ArgumentError, "State#bind requires a block"
  end

  s1 = set(**options)

  fetch(:value) { return s1 }.then do |value|
    block[value, s1, state: s1]
  end
end
combine(other) click to toggle source

Merges {self} with {other} and returns a new state

@param other [State]

@return [State]

# File lib/remap/state/extension.rb, line 147
def combine(other)
  merge(other) do |key, value1, value2|
    case [key, value1, value2]
    in [_, Hash => left, Hash => right]
      left.merge(right)
    in [:ids | :fatal_ids, _, right]
      right
    in [_, Array => left, Array => right]
      left + right
    in [:value, left, right]
      other.fatal!(
        "Could not merge [%s] (%s) with [%s] (%s)",
        left.formatted,
        left.class,
        right.formatted,
        right.class
      )
    in [Symbol, _, value]
      value
    end
  end._
end
element() click to toggle source

@return [Any]

# File lib/remap/state/extension.rb, line 384
def element
  fetch(:element)
end
execute(&block) click to toggle source

Execute {block} in the current context Only calls {block} if {#value} is defined

@yieldparam [T] @yieldreturn [U]

@return [State<U>]

# File lib/remap/state/extension.rb, line 293
def execute(&block)
  value = fetch(:value) { return self }

  path = catch :ignore do
    names = block.parameters.reduce([]) do |acc, (type, name)|
      case type
      in :keyreq
        acc + [name]
      else
        acc
      end
    end

    n1 = options.only(*names)
    n2 = only(*names)

    result = block[value, **n2, **n1] do |reason|
      return ignore!(reason)
    end

    return set(result)
  end

  set(path: path).ignore!("Undefined path")
rescue KeyError => e
  set(path: [e.key]).ignore!(e.message)
rescue IndexError => e
  ignore!(e.message)
end
failure(reason = Undefined) click to toggle source

Creates a failure from the current state

@param reason [String, Hash, Undefined]

@return [Failure]

# File lib/remap/state/extension.rb, line 403
def failure(reason = Undefined)
  failures = case [path, reason]
  in [_, Undefined]
    return Failure.new(failures: notices)
  in [_, Notice => notice]
    [notice]
  in [path, Array => reasons]
    reasons.map do |inner_reason|
      Notice.call(path: path, reason: inner_reason, **only(:value))
    end
  in [path, String => reason]
    [Notice.call(path: path, reason: reason, **only(:value))]
  in [path, Hash => errors]
    errors.paths.flat_map do |sufix|
      Array.wrap(errors.dig(*sufix)).map do |inner_reason|
        Notice.call(
          reason: inner_reason,
          path: path + sufix,
          **only(:value)
        )
      end
    end
  end

  Failure.new(failures: failures, notices: notices)
end
fatal!(...) click to toggle source

@see notice

# File lib/remap/state/extension.rb, line 53
def fatal!(...)
  fatal_id = fetch(:fatal_id) do
    raise ArgumentError, "Missing :fatal_id in %s" % formatted
  end

  throw fatal_id, Failure.new(failures: [notice(...)], notices: notices)
end
fatal_id() click to toggle source

@return [Symbol]

# File lib/remap/state/extension.rb, line 353
def fatal_id
  fetch(:fatal_id)
end
fmap(**options, &block) click to toggle source

Passes {#value} to block, if defined The return value is then wrapped in a state and returned with {options} combine into the state

@yieldparam value [T] @yieldparam self [State] @yieldparam error [Proc]

@yieldreturn [Y]

@return [State<Y>]

# File lib/remap/state/extension.rb, line 258
def fmap(**options, &block)
  bind(**options) do |input, state:|
    state.set(block[input, state, state: state])
  end
end
id() click to toggle source

@return [Symbol]

# File lib/remap/state/extension.rb, line 338
def id
  fetch(:id)
end
ids() click to toggle source

@return [Array<Symbol>]

# File lib/remap/state/extension.rb, line 348
def ids
  fetch(:ids)
end
ignore!(...) click to toggle source

@see notice

# File lib/remap/state/extension.rb, line 62
def ignore!(...)
  set(notice: notice(...)).except(:value).return!
end
index() click to toggle source

@return [Integer]

# File lib/remap/state/extension.rb, line 379
def index
  fetch(:index)
end
inspect() click to toggle source

@return [String]

# File lib/remap/state/extension.rb, line 138
def inspect
  "#<State %s>" % compact_blank.formatted
end
key() click to toggle source

@return [Any]

# File lib/remap/state/extension.rb, line 389
def key
  fetch(:key)
end
map(&block) click to toggle source

Iterates over {#value}

@yieldparam value [Any] @yieldparam key [Symbol] @yieldparam index [Integer]

@yieldreturn [State]

@return [State]

# File lib/remap/state/extension.rb, line 110
def map(&block)
  result = case self
  in { value: Array => array }
    array.each_with_index.each_with_object([]) do |(value, index), array|
      s1 = block[set(value, index: index)]

      if s1.key?(:value)
        array << s1[:value]
      end
    end
  in { value: Hash => hash }
    hash.each_with_object({}) do |(key, value), acc|
      s1 = block[set(value, key: key)]

      if s1.key?(:value)
        acc[key] = s1[:value]
      end
    end
  in { value: }
    fatal!("Expected an enumerable got %s", value.class)
  else
    return self
  end

  set(result)
end
mapper() click to toggle source

@return [Mapper::API]

# File lib/remap/state/extension.rb, line 343
def mapper
  fetch(:mapper)
end
notice(template, *values) click to toggle source

Creates a notice containing the given message

@param template [String] @param values [Array]

@return [Notice]

# File lib/remap/state/extension.rb, line 72
def notice(template, *values)
  Notice.call(only(:value, :path).merge(reason: template % values))
end
notices() click to toggle source

@return [Array<Notice>]

# File lib/remap/state/extension.rb, line 394
def notices
  fetch(:notices)
end
only(*path) click to toggle source

Restrict hash to passed key path

@param key [Symbol] to be kept @param rest [Array<Symbol>] of the key path

@example Select key path

{
  a: {
    b: :c
  },
  d: :e
}.only(:a, :b) # => { a: { b: :c } }

@returns [Hash] a hash containing the given path @raise Europace::Error when path doesn’t exist

# File lib/remap/state/extension.rb, line 48
def only(*path)
  dup.extract!(*path)
end
options() click to toggle source

Represents options to a mapper

@see Rule::Embed

@return [Hash]

# File lib/remap/state/extension.rb, line 362
def options
  fetch(:options)
end
path() click to toggle source

A list of keys representing the path to {#value}

@return [Array<Symbol, Integer, String>]

# File lib/remap/state/extension.rb, line 333
def path
  fetch(:path, EMPTY_ARRAY)
end
paths() click to toggle source

Returns a list of all key paths

@example Get paths

{
  a: {
    b: :c
  },
  d: :e
}.paths # => [[:a, :b], [:d]]

@return [Array<Array<Symbol>>] a list of key paths

# File lib/remap/state/extension.rb, line 23
def paths
  reduce(EMPTY_ARRAY) do |acc, (path, leaves)|
    if (paths = leaves.paths).empty?
      next acc + [[path]]
    end

    acc + paths.map { |inner| [path] + inner }
  end
end
remove_fatal_id() click to toggle source

@todo Merge with {#remove_id} @return [State]

# File lib/remap/state/extension.rb, line 191
def remove_fatal_id
  state = dup

  case state
  in { fatal_ids: [], fatal_id: }
    state.except!(:fatal_id)
  in { fatal_ids: ids, fatal_id: }
    state.merge!(fatal_ids: ids[1...], fatal_id: ids[0])
  in { fatal_ids: [] }
    state
  in { fatal_ids: }
    raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
  end

  state
end
remove_id() click to toggle source

@todo Merge with {#remove_fatal_id} @return [State]

# File lib/remap/state/extension.rb, line 172
def remove_id
  state = dup

  case state
  in { ids: [], id: }
    state.except!(:id)
  in { ids:, id: }
    state.merge!(ids: ids[1...], id: ids[0])
  in { ids: [] }
    state
  in { ids: }
    raise ArgumentError, "[BUG] #ids for state are set, but not #id: %s" % formatted
  end

  state
end
return!() click to toggle source

@raise [ArgumentError]

when {#id} is not defined

@private

# File lib/remap/state/extension.rb, line 434
def return!
  id = fetch(:id) do
    raise ArgumentError, "#id not defined for state [%s]" % [formatted]
  end

  throw id, remove_id
end
set(value = Undefined, **options) click to toggle source

Creates a new state with params

@param value [Any, Undefined] Used as {value:} @options [Hash] To be combine into {self}

@return [State]

# File lib/remap/state/extension.rb, line 214
def set(value = Undefined, **options)
  if value != Undefined
    return set(**options, value: value)
  end

  state = dup

  case [state, options]
  in [{notices:}, {notice: notice}]
    state.merge!(notices: notices + [notice])
  in [{value:}, {mapper:}]
    state.merge!(scope: value, mapper: mapper)
  in [{path: p1}, {path: p2}]
    state.merge!(path: p1 + p2)
  in [{path:}, {key:, value:}]
    state.merge!(path: path + [key], key: key, value: value)
  in [{path:}, {key:}]
    state.merge!(path: path + [key], key: key)
  in [{path:}, {index:, value:}]
    state.merge!(path: path + [index], element: value, index: index, value: value)
  in [{path:}, {index:}]
    state.merge!(path: path + [index], index: index)
  in [{ids:, id: old_id}, {id: new_id}]
    state.merge!(ids: [old_id] + ids, id: new_id)
  in [{fatal_ids:, fatal_id: old_id}, {fatal_id: new_id}]
    state.merge!(fatal_ids: [old_id] + fatal_ids, fatal_id: new_id)
  else
    state.merge!(options)
  end

  state
end
tap(&block) click to toggle source

Passes {#value} to block and returns {self}

@return [self]

Calls superclass method
# File lib/remap/state/extension.rb, line 326
def tap(&block)
  super { fmap(&block) }
end
to_hash() click to toggle source

Used by {#context} to create a limited context

@return [Hash]

Calls superclass method
# File lib/remap/state/extension.rb, line 369
def to_hash
  super.except(:options, :notices, :value, :id, :ids, :fatal_id, :fatal_ids)
end
value() click to toggle source

@return [Any]

# File lib/remap/state/extension.rb, line 374
def value
  fetch(:value)
end