lib/dry/validation/hint_compiler.rb in dry-validation-0.6.0 vs lib/dry/validation/hint_compiler.rb in dry-validation-0.7.0

- old
+ new

@@ -1,74 +1,124 @@ -require 'dry/validation/error_compiler' +require 'dry/validation/error_compiler/input' module Dry module Validation - class HintCompiler < ErrorCompiler - attr_reader :messages, :rules, :options + class HintCompiler < ErrorCompiler::Input + include Dry::Equalizer(:messages, :rules, :options) + attr_reader :rules, :excluded + + TYPES = { + none?: NilClass, + bool?: TrueClass, + str?: String, + int?: Fixnum, + float?: Float, + decimal?: BigDecimal, + date?: Date, + date_time?: DateTime, + time?: Time, + hash?: Hash, + array?: Array + }.freeze + + EXCLUDED = [:none?, :filled?, :key?].freeze + + def self.cache + @cache ||= ThreadSafe::Cache.new + end + def initialize(messages, options = {}) - @messages = messages - @options = Hash[options] + super(messages, { name: nil, input: nil }.merge(options)) @rules = @options.delete(:rules) + @excluded = @options.fetch(:excluded, EXCLUDED) + @val_type = options[:val_type] end def with(new_options) super(new_options.merge(rules: rules)) end def call - messages = Hash.new { |h, k| h[k] = [] } + self.class.cache.fetch_or_store(hash) do + super(rules) + end + end - rules.map { |node| visit(node) }.compact.each do |hints| - name, msgs = hints - messages[name].concat(msgs) + def visit_predicate(node) + predicate, _ = node + + val_type = TYPES[predicate] + + return with(val_type: val_type) if val_type + return {} if excluded.include?(predicate) + + super + end + + def visit_set(node) + result = node.map do |el| + visit(el) end + merge(result) + end - messages + def visit_each(node) + visit(node) end def visit_or(node) left, right = node - [visit(left), Array(visit(right)).flatten.compact].compact + merge([visit(left), visit(right)]) end def visit_and(node) left, right = node - [visit(left), Array(visit(right)).flatten.compact].compact + + result = visit(left) + + if result.is_a?(self.class) + result.visit(right) + else + visit(right) + end end - def visit_val(node) + def visit_implication(node) + _, right = node + visit(right) + end + + def visit_key(node) name, predicate = node - visit(predicate, name) + with(name: Array([*self.name, name])).visit(predicate) end + alias_method :visit_attr, :visit_key - def visit_predicate(node, name) - predicate_name, args = node + def visit_val(node) + visit(node) + end - lookup_options = options.merge(rule: name, arg_type: args[0].class) + def visit_schema(node) + DEFAULT_RESULT + end - template = messages[predicate_name, lookup_options] - predicate_opts = visit(node, args) - - return unless predicate_opts - - tokens = predicate_opts.merge(name: name) - - template % tokens + def visit_check(node) + DEFAULT_RESULT end - def visit_key(node) - name, _ = node - name + def visit_xor(node) + DEFAULT_RESULT end - def visit_attr(node) - name, _ = node - name + def visit_not(node) + DEFAULT_RESULT end - def method_missing(name, *args) - nil + private + + def merge(result) + super(result.reject { |el| el.is_a?(self.class) }) end end end end