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