class Dry::Schema::Messages::YAML

Plain YAML message backend

@api public

Constants

EMPTY_CONTEXT
LOCALE_TOKEN
TOKEN_REGEXP

Attributes

data[R]

Loaded localized message templates

@return [Hash]

t[R]

Translation function

@return [Proc]

Public Class Methods

build(options = EMPTY_HASH) click to toggle source

@api private

Calls superclass method Dry::Schema::Messages::Abstract::build
# File lib/dry/schema/messages/yaml.rb, line 37
def self.build(options = EMPTY_HASH)
  super do |config|
    config.default_locale = :en unless config.default_locale

    config.root = "%<locale>s.#{config.root}"

    config.rule_lookup_paths = config.rule_lookup_paths.map { |path|
      "%<locale>s.#{path}"
    }
  end
end
cache() click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 70
def self.cache
  @cache ||= Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
end
flat_hash(hash, path = [], keys = {}) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 50
def self.flat_hash(hash, path = [], keys = {})
  hash.each do |key, value|
    flat_hash(value, [*path, key], keys) if value.is_a?(Hash)

    if value.is_a?(String) && hash["text"] != value
      keys[[*path, key].join(DOT)] = {
        text: value,
        meta: EMPTY_HASH
      }
    elsif value.is_a?(Hash) && value["text"].is_a?(String)
      keys[[*path, key].join(DOT)] = {
        text: value["text"],
        meta: value.dup.delete_if { |k| k == "text" }.map { |k, v| [k.to_sym, v] }.to_h
      }
    end
  end
  keys
end
new(data: EMPTY_HASH, config: nil) click to toggle source

@api private

Calls superclass method
# File lib/dry/schema/messages/yaml.rb, line 75
def initialize(data: EMPTY_HASH, config: nil)
  super()
  @data = data
  @config = config if config
  @t = proc { |key, locale: default_locale| get("%<locale>s.#{key}", locale: locale) }
end

Public Instance Methods

get(key, options = EMPTY_HASH) click to toggle source

Get a message for the given key and its options

@param [Symbol] key @param [Hash] options

@return [String]

@api public

# File lib/dry/schema/messages/yaml.rb, line 102
def get(key, options = EMPTY_HASH)
  data[evaluated_key(key, options)]
end
interpolatable_data(key, options, **data) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 143
def interpolatable_data(key, options, **data)
  tokens = evaluation_context(key, options).fetch(:tokens)
  data.select { |k,| tokens.include?(k) }
end
interpolate(key, options, **data) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 149
def interpolate(key, options, **data)
  evaluator = evaluation_context(key, options).fetch(:evaluator)
  data.empty? ? evaluator.() : evaluator.(**data)
end
key?(key, options = EMPTY_HASH) click to toggle source

Check if given key is defined

@return [Boolean]

@api public

# File lib/dry/schema/messages/yaml.rb, line 111
def key?(key, options = EMPTY_HASH)
  data.key?(evaluated_key(key, options))
end
looked_up_paths(predicate, options) click to toggle source

Get an array of looked up paths

@param [Symbol] predicate @param [Hash] options

@return [String]

@api public

# File lib/dry/schema/messages/yaml.rb, line 90
def looked_up_paths(predicate, options)
  super.map { |path| path % {locale: options[:locale] || default_locale} }
end
merge(overrides) click to toggle source

Merge messages from an additional path

@param [String] overrides

@return [Messages::I18n]

@api public

# File lib/dry/schema/messages/yaml.rb, line 122
def merge(overrides)
  if overrides.is_a?(Hash)
    self.class.new(
      data: data.merge(self.class.flat_hash(overrides)),
      config: config
    )
  else
    self.class.new(
      data: Array(overrides).reduce(data) { |a, e| a.merge(load_translations(e)) },
      config: config
    )
  end
end
prepare() click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 137
def prepare
  @data = config.load_paths.map { |path| load_translations(path) }.reduce({}, :merge)
  self
end

Private Instance Methods

cache() click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 176
def cache
  @cache ||= self.class.cache[self]
end
evaluated_key(key, options) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 190
def evaluated_key(key, options)
  return key unless key.include?(LOCALE_TOKEN)

  key % {locale: options[:locale] || default_locale}
end
evaluation_context(key, options) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 157
      def evaluation_context(key, options)
        cache.fetch_or_store(get(key, options).fetch(:text)) do |input|
          tokens = input.scan(TOKEN_REGEXP).flatten(1).map(&:to_sym).to_set
          text = input.gsub("%", "#")

          # rubocop:disable Security/Eval
          evaluator = eval(<<~RUBY, EMPTY_CONTEXT, __FILE__, __LINE__ + 1)
            -> (#{tokens.map { |token| "#{token}:" }.join(", ")}) { "#{text}" }
          RUBY
          # rubocop:enable Security/Eval

          {
            tokens: tokens,
            evaluator: evaluator
          }
        end
      end
load_translations(path) click to toggle source

@api private

# File lib/dry/schema/messages/yaml.rb, line 181
def load_translations(path)
  data = self.class.flat_hash(YAML.load_file(path))

  return data unless custom_top_namespace?(path)

  data.map { |k, v| [k.gsub(DEFAULT_MESSAGES_ROOT, config.top_namespace), v] }.to_h
end