# typed: strict module DataModel # Error is a class that holds errors. class Error extend T::Sig TErrorList = T.type_alias { T::Array[TError] } TErrorMap = T.type_alias { T::Hash[Symbol, TErrorList] } sig { void } def initialize @base = T.let([], TErrorList) @children = T.let({}, TErrorMap) end # errors related to the object as a whole sig { returns(TErrorList) } def base return @base end # errors related children sig { returns(TErrorMap) } def children return @children end # all errors sig { returns(TErrorMap) } def all return children.merge(base:) end alias to_h all # Returns true if any errors are present. sig { params(blk: T.nilable(T.proc.params(error: TError).returns(T::Boolean))).returns(T::Boolean) } def any?(&blk) if !blk return !@base.empty? || !@children.empty? end any = T.let(false, T::Boolean) for error_list in all.values any = error_list.any?(&blk) if any break end end return any end sig { returns(T::Boolean) } def empty? !any? end # Add an error to the error list. sig { params(err: TError, child: T.nilable(T.any(Symbol, T::Array[Symbol]))).void } def add(err, child: nil) if child.is_a?(Array) child = child.join(".").to_sym end if child == :base raise "child errors may not be named :base" end errs = child ? @children[child] ||= [] : @base errs.push(err) end sig { params(name: Symbol, child: Error).void } def merge_child(name, child) if !child.any? return end for (key, error_list) in child.all for error in error_list add(error, child: [name, key]) end end end sig { params(blk: T.proc.params(context: Object, type: Symbol).returns(Object)).void } def transform_context(&blk) for error in @base key, context = error error[1] = blk.call(context, key) end end sig { params(blk: T.proc.params(context: Object, type: Symbol).returns(Object)).void } def transform_child_context(&blk) for error_list in @children.values for error in error_list key, context = error error[1] = blk.call(context, key) end end end end end