Sha256: 3d2ccf70a3f04ea294cfab4e2ba91129c72fe9aa6e51e41a9744ef92e511cda1

Contents?: true

Size: 1.71 KB

Versions: 1

Compression:

Stored size: 1.71 KB

Contents

# typed: false
module Repeatable
  class Parser
    extend T::Sig

    sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).void }
    def initialize(hash)
      @hash = hash
    end

    sig { params(hash: T::Hash[T.any(String, Symbol), T.untyped]).returns(Expression::Base) }
    def self.call(hash)
      new(hash).call
    end

    sig { returns(Expression::Base) }
    def call
      build_expression(hash)
    end

    private

    attr_reader :hash

    def build_expression(hash)
      if hash.length != 1
        fail(ParseError, "Invalid expression: '#{hash}' must have single key and value")
      else
        expression_for(*hash.first)
      end
    end

    def expression_for(key, value)
      klass = expression_klass(key.to_s)
      case klass
      when nil
        fail(ParseError, "Unknown mapping: Can't map key '#{key.inspect}' to an expression class")
      when Repeatable::Expression::Set
        args = value.map { |hash| build_expression(hash) }
        klass.new(*args)
      when Repeatable::Expression::Difference
        value = symbolize_keys(value)
        klass.new(
          included: build_expression(value[:included]),
          excluded: build_expression(value[:excluded])
        )
      else
        klass.new(**symbolize_keys(value))
      end
    end

    def symbolize_keys(hash)
      hash.each_with_object({}) { |(k, v), a| a[k.to_sym] = v }
    end

    def expression_klass(string)
      camel_cased_string = string
        .capitalize
        .gsub(/(?:_)(?<word>[a-z\d]+)/i) { T.must(Regexp.last_match)[:word]&.capitalize }
      Repeatable::Expression.const_get(camel_cased_string)
    rescue NameError => e
      raise if e.name && e.name.to_s != camel_cased_string
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
repeatable-1.1.0 lib/repeatable/parser.rb