lib/dry/schema/message_compiler.rb in dry-schema-0.3.0 vs lib/dry/schema/message_compiler.rb in dry-schema-0.4.0

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require 'dry/initializer' + require 'dry/schema/constants' require 'dry/schema/message' require 'dry/schema/message_set' require 'dry/schema/message_compiler/visitor_opts' @@ -9,32 +11,47 @@ module Schema # Compiles rule results AST into human-readable format # # @api private class MessageCompiler - attr_reader :messages, :options, :locale, :default_lookup_options + extend Dry::Initializer + resolve_key_predicate = proc { |node, opts| + *arg_vals, val = node.map(&:last) + [[*opts.path, arg_vals[0]], arg_vals[1..arg_vals.size - 1], val] + } + + resolve_predicate = proc { |node, opts| + [Array(opts.path), *node.map(&:last)] + } + + DEFAULT_PREDICATE_RESOLVERS = Hash + .new(resolve_predicate).update(key?: resolve_key_predicate).freeze + EMPTY_OPTS = VisitorOpts.new - LIST_SEPARATOR = ', ' + param :messages + + option :full, default: -> { false } + option :locale, default: -> { :en } + option :predicate_resolvers, default: -> { DEFAULT_PREDICATE_RESOLVERS } + + attr_reader :options + + attr_reader :default_lookup_options + # @api private - def initialize(messages, options = {}) - @messages = messages + def initialize(messages, options = EMPTY_HASH) + super @options = options - @full = @options.fetch(:full, false) - @locale = @options[:locale] - @default_lookup_options = @locale ? { locale: locale } : EMPTY_HASH + @default_lookup_options = options[:locale] ? { locale: locale } : EMPTY_HASH end # @api private - def full? - @full - end - - # @api private def with(new_options) return self if new_options.empty? + self.class.new(messages, options.merge(new_options)) end # @api private def call(ast) @@ -54,11 +71,11 @@ rule, other = node visit(other, opts.(rule: rule)) end # @api private - def visit_hint(node, opts) + def visit_hint(*) nil end # @api private def visit_not(node, opts) @@ -79,11 +96,11 @@ # @api private def visit_or(node, opts) left, right = node.map { |n| visit(n, opts) } if [left, right].flatten.map(&:path).uniq.size == 1 - Message::Or.new(left, right, -> k { messages[k, default_lookup_options] }) + Message::Or.new(left, right, proc { |k| messages.translate(k, default_lookup_options) }) elsif right.is_a?(Array) right else [left, right].flatten.max end @@ -95,37 +112,27 @@ self.class.new(messages.namespaced(ns), options).visit(rest, opts) end # @api private def visit_predicate(node, opts) - base_opts = opts.dup predicate, args = node - *arg_vals, val = args.map(&:last) tokens = message_tokens(args) + path, *arg_vals, input = predicate_resolvers[predicate].(args, opts) - input = val != Undefined ? val : nil + options = opts.dup.update( + path: path.last, **tokens, **lookup_options(arg_vals: arg_vals, input: input) + ).to_h - options = base_opts.update(lookup_options(arg_vals: arg_vals, input: input)) - msg_opts = options.update(tokens) + template = messages[predicate, options] || raise(MissingMessageError, path) - rule = msg_opts[:rule] - path = msg_opts[:path] + text = message_text(template, tokens, options) - template = messages[rule] || messages[predicate, msg_opts] - - unless template - raise MissingMessageError, "message for #{predicate} was not found" - end - - text = message_text(rule, template, tokens, options) - message_type(options)[ predicate, path, text, args: arg_vals, - input: input, - rule: rule || msg_opts[:name] + input: input ] end # @api private def message_type(*) @@ -154,41 +161,39 @@ left, right = node [visit(left, opts), visit(right, opts)].uniq end # @api private - def lookup_options(arg_vals: [], input: nil) + def lookup_options(arg_vals:, input:) default_lookup_options.merge( arg_type: arg_vals.size == 1 && arg_vals[0].class, - val_type: input.class + val_type: input.equal?(Undefined) ? NilClass : input.class ) end # @api private - def message_text(rule, template, tokens, opts) + def message_text(template, tokens, options) text = template[template.data(tokens)] - if full? - rule_name = rule ? (messages.rule(rule, opts) || rule) : (opts[:name] || opts[:path].last) - "#{rule_name} #{text}" - else - text - end + return text unless full + + rule = options[:path] + "#{messages.rule(rule, options) || rule} #{text}" end # @api private def message_tokens(args) - args.each_with_object({}) { |arg, hash| + args.each_with_object({}) do |arg, hash| case arg[1] when Array hash[arg[0]] = arg[1].join(LIST_SEPARATOR) when Range hash["#{arg[0]}_left".to_sym] = arg[1].first hash["#{arg[0]}_right".to_sym] = arg[1].last else hash[arg[0]] = arg[1] end - } + end end end end end