lib/metaractor/errors.rb in metaractor-2.1.1 vs lib/metaractor/errors.rb in metaractor-3.0.0

- old
+ new

@@ -2,17 +2,76 @@ require 'forwardable' module Metaractor class Errors extend Forwardable + class Error + attr_reader :value, :object + + def initialize(value:, object: nil) + @value = value + @object = object + end + + def generate_message(path_elements:) + if @value.is_a? Symbol + defaults = [] + + if object.class.respond_to?(:i18n_parent_names) && + !object.class.i18n_parent_names.empty? + + names = object.class.i18n_parent_names + until names.empty? + defaults << ['errors', names.join('.'), 'parameters', path_elements.join('.'), @value.to_s].reject do |item| + item.nil? || item == '' + end.join('.').to_sym + names.pop + end + end + + unless path_elements.empty? + defaults << :"errors.parameters.#{path_elements.join('.')}.#{@value}" + end + defaults << :"errors.parameters.#{@value}" + + key = defaults.shift + I18n.translate( + key, + default: defaults, + error_key: @value, + parameter: path_elements.last + ) + else + "#{path_elements.join('.')} #{@value}".lstrip + end + end + + def ==(other) + if other.is_a?(self.class) + @value == other.value + else + @value == other + end + end + alias eql? == + + def hash + @value.hash + end + + def inspect + "(Error) #{@value.inspect}" + end + end + def initialize @tree = Sycamore::Tree.new end - def_delegators :@tree, :to_h, :empty? + def_delegators :@tree, :empty? - def add(error: {}, errors: {}) + def add(error: {}, errors: {}, object: nil) trees = [] [error, errors].each do |h| tree = nil if h.is_a? Metaractor::Errors tree = Sycamore::Tree.from(h.instance_variable_get(:@tree)) @@ -27,11 +86,21 @@ trees << tree end end trees.each do |tree| - @tree.add(tree) + tree.each_path do |path| + node = path.node + unless node.is_a?(Error) + node = Error.new( + value: path.node, + object: object + ) + end + + @tree[path.parent] << node + end end @tree.compact end def full_messages(tree = @tree) @@ -56,23 +125,29 @@ def dig(*path) result = @tree.dig(*path) if result.strict_leaves? - result.nodes + unwrapped_enum(result.nodes) else - result.to_h + unwrapped_tree(result).to_h end end alias [] dig def include?(*elements) if elements.size == 1 && elements.first.is_a?(Hash) - @tree.include?(*elements) + unwrapped_tree.include?(*elements) else - full_messages.include?(*elements) + if elements.all? {|e| e.is_a? String } + full_messages.include?(*elements) + else + elements.all? do |element| + @tree.include_path?(element) + end + end end end def slice(*paths) new_tree = Sycamore::Tree.new @@ -81,22 +156,68 @@ if @tree.include_path?(path) new_tree[path] = @tree[path].dup end end - new_tree.to_h + unwrapped_tree(new_tree).to_h end + def to_h(unwrap: true) + if unwrap + unwrapped_tree.to_h + else + @tree.to_h + end + end + + def inspect + str = "<##{self.class.name}: " + + if !self.empty? + str << "Errors:\n" + str << Metaractor.format_hash(to_h(unwrap: false)) + str << "\n" + end + + str << ">" + str + end + private def message_from_path(path) path_elements = [] path.parent&.each_node do |node| unless node == :base path_elements << node.to_s end end - "#{path_elements.join('.')} #{path.node.to_s}".lstrip + path.node.generate_message(path_elements: path_elements) end + + def unwrapped_tree(orig_tree = @tree) + tree = Sycamore::Tree.new + orig_tree.each_path do |path| + node = path.node + if node.is_a? Error + node = node.value + end + + tree[path.parent] << node + end + + tree + end + + def unwrapped_enum(orig) + orig.map do |element| + if element.is_a? Error + element.value + else + element + end + end + end + end end