class Frise::Loader

The entrypoint for loading configs from files according to the conventions defined for Frise.

The load method loads a configuration file, merges the applicable includes and validates its schema.

Public Class Methods

new(include_sym: '$include', content_include_sym: '$content_include', schema_sym: '$schema', delete_sym: '$delete', pre_loaders: [], validators: nil, exit_on_fail: true) click to toggle source
# File lib/frise/loader.rb, line 13
def initialize(include_sym: '$include',
               content_include_sym: '$content_include',
               schema_sym: '$schema',
               delete_sym: '$delete',
               pre_loaders: [],
               validators: nil,
               exit_on_fail: true)

  @include_sym = include_sym
  @content_include_sym = content_include_sym
  @schema_sym = schema_sym
  @delete_sym = delete_sym
  @pre_loaders = pre_loaders
  @validators = validators
  @exit_on_fail = exit_on_fail

  @defaults_loader = DefaultsLoader.new(
    include_sym: include_sym,
    content_include_sym: content_include_sym,
    schema_sym: schema_sym,
    delete_sym: delete_sym
  )
end

Public Instance Methods

load(config_file, global_vars = {}) click to toggle source
# File lib/frise/loader.rb, line 37
def load(config_file, global_vars = {})
  config = Parser.parse(config_file, global_vars)
  return nil unless config

  @pre_loaders.each do |pre_loader|
    config = pre_loader.call(config)
  end

  config = process_includes(config, [], config, global_vars) unless @include_sym.nil?
  config = process_schemas(config, [], global_vars) unless @schema_sym.nil?
  config
end

Private Instance Methods

build_path(at_path) click to toggle source

builds a user-friendly string indicating a path

# File lib/frise/loader.rb, line 196
def build_path(at_path)
  at_path.empty? ? '<root>' : at_path.join('.')
end
build_symbol_table(root_config, at_path, config, global_vars, include_conf) click to toggle source

builds the symbol table for the Liquid renderization of a file, based on:

- `root_config`: the root of the whole config
- `at_path`: the current path
- `config`: the config subtree being built
- `global_vars`: the global variables
- `include_conf`: the $include or $content_include configuration
# File lib/frise/loader.rb, line 184
def build_symbol_table(root_config, at_path, config, global_vars, include_conf)
  extra_vars = (include_conf['vars'] || {}).map { |k, v| [k, root_config.dig(*v.split('.'))] }.to_h
  extra_consts = include_conf['constants'] || {}

  omit_deleted(config ? merge_at(root_config, at_path, config) : root_config)
    .merge(global_vars)
    .merge(extra_vars)
    .merge(extra_consts)
    .merge('_this' => config)
end
extract_content_include(config, at_path) click to toggle source
# File lib/frise/loader.rb, line 138
def extract_content_include(config, at_path)
  extract_include_base(config, @content_include_sym, at_path)
end
extract_include(config, at_path) click to toggle source
# File lib/frise/loader.rb, line 134
def extract_include(config, at_path)
  extract_include_base(config, @include_sym, at_path)
end
extract_include_base(config, sym, at_path) click to toggle source
# File lib/frise/loader.rb, line 142
def extract_include_base(config, sym, at_path)
  extract_special(config, sym, at_path) do |value|
    case value
    when Hash then value
    when String then { 'file' => value }
    else raise "At #{build_path(at_path)}: illegal value for a #{sym} element: #{value.inspect}"
    end
  end
end
extract_schema(config, at_path) click to toggle source
# File lib/frise/loader.rb, line 125
def extract_schema(config, at_path)
  extract_special(config, @schema_sym, at_path) do |value|
    case value
    when String then value
    else raise "At #{build_path(at_path)}: illegal value for a #{@schema_sym} element: #{value.inspect}"
    end
  end
end
extract_special(config, key, at_path) { |e| ... } click to toggle source
# File lib/frise/loader.rb, line 152
def extract_special(config, key, at_path)
  case config[key]
  when nil then [config, []]
  when Array then [config.reject { |k| k == key }, config[key].map { |e| yield e }]
  else raise "At #{build_path(at_path)}: illegal value for #{key}: #{config[key].inspect}"
  end
end
merge_at(config, at_path, to_merge) click to toggle source

merges the `to_merge` value on `config` at path `at_path`

# File lib/frise/loader.rb, line 161
def merge_at(config, at_path, to_merge)
  return config.merge(to_merge) if at_path.empty?
  head, *tail = at_path
  config.merge(head => merge_at(config[head], tail, to_merge))
end
omit_deleted(config) click to toggle source

returns the config without the keys whose values are @delete_sym

# File lib/frise/loader.rb, line 168
def omit_deleted(config)
  config.each_with_object({}) do |(k, v), new_hash|
    if v.is_a?(Hash)
      new_hash[k] = omit_deleted(v)
    else
      new_hash[k] = v unless v == @delete_sym
    end
  end
end
process_includes(config, at_path, root_config, global_vars, include_confs_stack = []) click to toggle source
# File lib/frise/loader.rb, line 52
def process_includes(config, at_path, root_config, global_vars, include_confs_stack = [])
  return config unless config.class == Hash

  # process $content_include directives
  config, content_include_confs = extract_content_include(config, at_path)
  unless content_include_confs.empty?
    raise "At #{build_path(at_path)}: a #{@content_include_sym} must not have any sibling key" unless config.empty?

    content = ''
    content_include_confs.each do |include_conf|
      symbol_table = build_symbol_table(root_config, at_path, nil, global_vars, include_conf)
      content += Parser.parse_as_text(include_conf['file'], symbol_table) || ''
    end
    return content
  end

  # process $include directives
  config, next_include_confs = extract_include(config, at_path)
  include_confs = next_include_confs + include_confs_stack
  res = if include_confs.empty?
          config.map { |k, v| [k, process_includes(v, at_path + [k], root_config, global_vars)] }.to_h
        else
          Lazy.new do
            include_conf = include_confs.first
            rest_include_confs = include_confs[1..-1]
            symbol_table = build_symbol_table(root_config, at_path, config, global_vars, include_conf)
            included_config = Parser.parse(include_conf['file'], symbol_table)
            config = @defaults_loader.merge_defaults_obj(config, included_config)
            process_includes(config, at_path, merge_at(root_config, at_path, config), global_vars, rest_include_confs)
          end
        end
  @delete_sym.nil? ? res : omit_deleted(res)
end
process_schema_includes(schema, at_path, global_vars) click to toggle source
# File lib/frise/loader.rb, line 86
def process_schema_includes(schema, at_path, global_vars)
  return schema unless schema.class == Hash

  schema, included_schemas = extract_include(schema, at_path)
  if included_schemas.empty?
    schema.map { |k, v| [k, process_schema_includes(v, at_path + [k], global_vars)] }.to_h
  else
    included_schemas.each do |defaults_conf|
      schema = Parser.parse(defaults_conf['file'], global_vars).merge(schema)
    end
    process_schema_includes(schema, at_path, global_vars)
  end
end
process_schemas(config, at_path, global_vars) click to toggle source
# File lib/frise/loader.rb, line 100
def process_schemas(config, at_path, global_vars)
  return config unless config.class == Hash

  config = config.map do |k, v|
    new_v = process_schemas(v, at_path + [k], global_vars)
    return nil if !v.nil? && new_v.nil?
    [k, new_v]
  end.to_h

  config, schema_files = extract_schema(config, at_path)
  schema_files.each do |schema_file|
    schema = Parser.parse(schema_file, global_vars)
    schema = process_schema_includes(schema, at_path, global_vars)

    errors = Validator.validate_obj(config,
                                    schema,
                                    path_prefix: at_path,
                                    validators: @validators,
                                    print: @exit_on_fail,
                                    fatal: @exit_on_fail)
    return nil if errors.any?
  end
  config
end