module FunctionalLightService

Constants

Option
Result
VERSION

Public Class Methods

guard_context(obj, args) click to toggle source
# File lib/functional-light-service/functional/enum.rb, line 180
def self.guard_context(obj, args)
  if obj.is_a?(FunctionalLightService::EnumBuilder::DataType::Binary)
    Struct.new(*args).new(*obj.value.values)
  else
    Struct.new(*args).new(obj.value)
  end
end
match(obj, &block) click to toggle source
# File lib/functional-light-service/functional/enum.rb, line 139
def self.match(obj, &block)
  caller_ctx = block.binding.eval 'self'

  matcher = self::Matcher.new(obj)
  matcher.instance_eval(&block)

  variants_in_match = matcher.matches.collect do |e|
    e[1].name.split('::')[-1].to_sym
  end.uniq.sort
  variants_not_covered = variants - variants_in_match
  unless variants_not_covered.empty?
    raise Enum::MatchError, "Match is non-exhaustive, #{variants_not_covered} not covered"
  end

  type_matches = matcher.matches.select { |r| r[0].is_a?(r[1]) }

  type_matches.each do |match|
    obj, _type, block, args, guard = match

    return caller_ctx.instance_eval(&block) if args.count.zero?

    if args.count != obj.args.count
      msg = "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
      raise Enum::MatchError, msg
    end

    guard_ctx = guard_context(obj, args)
    return caller_ctx.instance_exec(* obj.wrapped_values, &block) unless guard

    if guard && guard_ctx.instance_exec(obj, &guard)
      return caller_ctx.instance_exec(* obj.wrapped_values, &block)
    end
  end

  raise Enum::MatchError, "No match could be made"
end
new(obj) click to toggle source
# File lib/functional-light-service/functional/enum.rb, line 194
def initialize(obj)
  @obj = obj
  @matches = []
  @vars = []
end
variants() click to toggle source
# File lib/functional-light-service/functional/enum.rb, line 176
def self.variants
  constants - %i[Matcher MatchError]
end

Public Instance Methods

+(other) click to toggle source
# File lib/functional-light-service/functional/option.rb, line 68
def +(other)
  match do
    None() { other }
    Some(where { !other.is_a?(Option) }) { |_| raise TypeError, "Other must be an #{Option}" }
    Some(where { other.some? }) { |s| Option::Some.new(s + other.value) }
    Some() { |_| self }
  end
end
<<(proc = nil, &block)
Alias for: pipe
>=(proc = nil, &block)
Alias for: try
>>(&fn)
Alias for: map
and(other) click to toggle source
# File lib/functional-light-service/functional/result.rb, line 59
def and(other)
  unless other.is_a? Result
    msg = "Expected #{other.inspect} to be a Result"
    raise FunctionalLightService::Monad::NotMonadError, msg
  end

  match do
    Success() { |_| other }
    Failure() { |_| self }
  end
end
and_then(&fn)
Alias for: map
empty?()
Alias for: none?
enum(&block) click to toggle source

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity

# File lib/functional-light-service/functional/enum.rb, line 135
def enum(&block)
  mod = Class.new do # the enum to be built
    private_class_method :new

    def self.match(obj, &block)
      caller_ctx = block.binding.eval 'self'

      matcher = self::Matcher.new(obj)
      matcher.instance_eval(&block)

      variants_in_match = matcher.matches.collect do |e|
        e[1].name.split('::')[-1].to_sym
      end.uniq.sort
      variants_not_covered = variants - variants_in_match
      unless variants_not_covered.empty?
        raise Enum::MatchError, "Match is non-exhaustive, #{variants_not_covered} not covered"
      end

      type_matches = matcher.matches.select { |r| r[0].is_a?(r[1]) }

      type_matches.each do |match|
        obj, _type, block, args, guard = match

        return caller_ctx.instance_eval(&block) if args.count.zero?

        if args.count != obj.args.count
          msg = "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
          raise Enum::MatchError, msg
        end

        guard_ctx = guard_context(obj, args)
        return caller_ctx.instance_exec(* obj.wrapped_values, &block) unless guard

        if guard && guard_ctx.instance_exec(obj, &guard)
          return caller_ctx.instance_exec(* obj.wrapped_values, &block)
        end
      end

      raise Enum::MatchError, "No match could be made"
    end

    def self.variants
      constants - %i[Matcher MatchError]
    end

    def self.guard_context(obj, args)
      if obj.is_a?(FunctionalLightService::EnumBuilder::DataType::Binary)
        Struct.new(*args).new(*obj.value.values)
      else
        Struct.new(*args).new(obj.value)
      end
    end
  end
  enum = EnumBuilder.new(mod)
  enum.instance_eval(&block)

  type_variants = mod.constants

  matcher = Class.new do
    def initialize(obj)
      @obj = obj
      @matches = []
      @vars = []
    end

    attr_reader :matches, :vars

    def where(&guard)
      guard
    end

    type_variants.each do |m|
      define_method(m) do |guard = nil, &inner_block|
        raise ArgumentError, "No block given to `#{m}`" if inner_block.nil?

        params_spec = inner_block.parameters
        if params_spec.any? { |spec| spec.size < 2 }
          msg = "Unnamed param found in block parameters: #{params_spec.inspect}"
          raise ArgumentError, msg
        end
        if params_spec.any? { |spec| spec[0] != :req && spec[0] != :opt }
          msg = "Only :req & :opt params allowed; parameters=#{params_spec.inspect}"
          raise ArgumentError, msg
        end

        args = params_spec.map { |spec| spec[1] }

        type = mod.const_get(m)

        guard = nil if guard && !guard.is_a?(Proc)

        @matches << [@obj, type, inner_block, args, guard]
      end
    end
  end

  mod.const_set(:Matcher, matcher)

  type_variants.each do |variant|
    mod.singleton_class.class_exec do
      define_method(variant) do |*args|
        const_get(variant).new(*args)
      end
    end
  end
  mod
end
failure?() click to toggle source
# File lib/functional-light-service/functional/result.rb, line 43
def failure?
  is_a? Result::Failure
end
fmap() { |s| ... } click to toggle source
# File lib/functional-light-service/functional/option.rb, line 33
def fmap
  match do
    Some() { |s| self.class.new(yield(s)) }
    None() { self }
  end
end
impl(enum_type, &block) click to toggle source

rubocop:enable Metrics/AbcSize rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/MethodLength rubocop:enable Metrics/PerceivedComplexity

# File lib/functional-light-service/functional/enum.rb, line 247
def impl(enum_type, &block)
  enum_type.variants.each do |v|
    name = "#{enum_type.name}::#{v}"
    type = Kernel.eval(name)
    type.class_eval(&block)
  end
end
map(&fn) click to toggle source
# File lib/functional-light-service/functional/option.rb, line 40
def map(&fn)
  match do
    Some() { |_s| bind(&fn) }
    None() { self }
  end
end
Also aliased as: >>, and_then
map_err(proc = nil, &block) click to toggle source
# File lib/functional-light-service/functional/result.rb, line 26
def map_err(proc = nil, &block)
  failure? ? bind(proc || block) : self
end
Also aliased as: or_else
none?() click to toggle source
# File lib/functional-light-service/functional/option.rb, line 51
def none?
  is_a? Option::None
end
Also aliased as: empty?
or(other) click to toggle source
# File lib/functional-light-service/functional/result.rb, line 47
def or(other)
  unless other.is_a? Result
    msg = "Expected #{other.inspect} to be a Result"
    raise FunctionalLightService::Monad::NotMonadError, msg
  end

  match do
    Success() { |_| self }
    Failure() { |_| other }
  end
end
or_else(proc = nil, &block)
Alias for: map_err
pipe(proc = nil, &block) click to toggle source
# File lib/functional-light-service/functional/result.rb, line 32
def pipe(proc = nil, &block)
  (proc || block).call(self)
  self
end
Also aliased as: <<
some?() click to toggle source
# File lib/functional-light-service/functional/option.rb, line 47
def some?
  is_a? Option::Some
end
success?() click to toggle source
# File lib/functional-light-service/functional/result.rb, line 39
def success?
  is_a? Result::Success
end
try(proc = nil, &block) click to toggle source
# File lib/functional-light-service/functional/result.rb, line 85
def try(proc = nil, &block)
  map(proc, &block)
rescue StandardError => e
  Result::Failure.new(e)
end
Also aliased as: >=
value_or(n) click to toggle source
# File lib/functional-light-service/functional/option.rb, line 57
def value_or(n)
  match do
    Some() { |s| s }
    None() { n }
  end
end
value_to_a() click to toggle source
# File lib/functional-light-service/functional/option.rb, line 64
def value_to_a
  @value
end
where(&guard) click to toggle source
# File lib/functional-light-service/functional/enum.rb, line 202
def where(&guard)
  guard
end