Project

frise

0.0
No release in over a year
Ruby config library with schema validation, default values and templating
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.0
~> 13.0
~> 3.9
~> 1.10.0
~> 0.18

Runtime

~> 5.3.0
 Project Readme

Frise

Build Status Coverage Status Gem Version

Frise is a library for loading configuration files as native Ruby structures. Besides reading and parsing the files themselves, it also:

  • Allows defining other files to be merged anywhere in the config, which can be used to provide default values specified in another file or set of files;
  • Interprets Liquid templates in configs and defaults;
  • Validates the loaded config according to a schema file or set of files.

Install

gem install frise

Usage

Basic configs

The simplest example would be to load a simple configuration from a file:

require 'frise'

loader = Frise::Loader.new
loader.load('example/config.yml')
# => {"movies"=>
#     [{"title"=>"The Shawshank Redemption",
#       "year"=>1994,
#       "categories"=>["Crime", "Drama"],
#       "rating"=>9.3},
#      {"title"=>"The Godfather",
#       "year"=>1972,
#       "director"=>"Francis Ford Coppola",
#       "categories"=>["Crime", "Drama"],
#       "rating"=>9.2}]}

Currently Frise only supports YAML files, but it may support JSON and other formats in the future.

Default values

By using the $include directive pointing to the files where default values can be found (in this example, example/_defaults/config.yml), Frise can handle its application internally on load time:

loader.load('example/config_with_defaults.yml')
# => {"movies"=>
#     [{"title"=>"The Shawshank Redemption",
#       "year"=>1994,
#       "categories"=>["Crime", "Drama"],
#       "rating"=>9.3,
#       "director"=>"N/A"},
#      {"title"=>"The Godfather",
#       "year"=>1972,
#       "director"=>"Francis Ford Coppola",
#       "categories"=>["Crime", "Drama"],
#       "rating"=>9.2}],
#    "ui"=>
#     {"default_movie"=>"The Shawshank Redemption",
#      "filter_from"=>1972,
#      "filter_to"=>1994}}

Note that files with default values follow exactly the same structure of the config file itself. Special values such as $all allow users to define default values for all elements of an object or array. Liquid templates are also used to define some defaults as a function of other objects of the config.

Schemas

Additionally, configuration files can also be validated against a schema. By specifying $schema in the config, users can provide schema files such as example/_schemas/config.yml:

loader.load('example/config_with_defaults_and_schema.yml')
# {"movies"=>
#   [{"title"=>"The Shawshank Redemption",
#     "year"=>1994,
#     "categories"=>["Crime", "Drama"],
#     "rating"=>9.3,
#     "director"=>"N/A"},
#    {"title"=>"The Godfather",
#     "year"=>1972,
#     "director"=>"Francis Ford Coppola",
#     "categories"=>["Crime", "Drama"],
#     "rating"=>9.2}],
#  "ui"=>
#   {"default_movie"=>"The Shawshank Redemption",
#    "filter_from"=>1972,
#    "filter_to"=>1994}}

If this config is loaded without the defaults instead, there are now required values that are missing and Frise by default prints a summary of the errors and terminates the program:

loader.load('example/config_with_schema.yml')
# 2 config error(s) found:
#  - At movies.0.director: missing required value
#  - At ui: missing required value

Once more, the structure of the schema mimics the structure of the config itself, making it easy to write schemas first and create a config scaffold from its schema later.

Users can check a whole range of properties in config values besides their type: optional values, hashes with validated keys, hashes with unknown keys and even custom validations are also supported. The specification of the validator provides various examples of schemas and describes the behavior of each value (more documentation will be written soon).

Other features

Users can also define custom code to be run before defaults and schemas are applied and can even do each of the steps separately. Additionally, defaults and schemas can be loaded at a specific path inside an existing Ruby object. The Loader class provides high-level methods to access those features, while lower-level functionality can be accessed through Parser, DefaultsLoader and Validator.