Skip to content

Repository files navigation

🔥 Nero

Gem Version

Nero is a RubyGem that offers declarative YAML-tags to simplify config files, e.g. for requiring and coercion of env-vars.
Additionally, it allows you to create your own.

Sample:

development:
# env-var with default valuesecret: !env [SECRET, "dummy"]# optional env-var with coerciondebug?: !env/bool? DEBUGproduction:
# required env-var (not required during development)secret: !env SECRET# coercionmax_threads: !env/integer [MAX_THREADS, 5]# refer to other keysmin_threads: !env/integer [MIN_THREADS, !ref max_threads ]# descriptive namesasset_folder: !path/rails_root [ public/assets ]# easy to add custom tagscache_ttl: !duration [2, hours]

Highlights

  • 💎 declarative YAML-tags for e.g. requiring and coercing env-vars
  • 🛠️ add custom tags
  • 🛤️ Rails.application.config_for drop-in
  • ♻️ Zeitwerk-only dependency

Installation

Install the gem and add to the application's Gemfile by executing:

bundle add nero

Configuration

Nero.configuredo |nero|
# Path that `Nero.config_for` uses to resolve Symbol or String files, e.g. `Nero.config_for(:app)`nero.config_dir="config"# Add custom tags (also see section about custom tags)nero.add_tag("upcase")do |tag|
# tag is an instance of [Nero::BaseTag](https://eval.github.io/nero/Nero/BaseTag.html).tag.args.join.upcaseendend

Usage

Warning

It's early days - the API and included tags will certainly change. Check the CHANGELOG when upgrading.

loading a config

Given the following config:

# config/app.ymldevelopment:
# env-var with a fallbacksecret: !env [SECRET, "dummy"]# Though the default is false, explicitly providing "false"/"off"/"n"/"no" also works.debug?: !env/bool? DEBUGproduction:
# fail-fast on absence of SECRETsecret: !env SECRET# always an integermax_threads: !env/integer [MAX_THREADS, 5]

Loading this config:

# Loading developmentNero.load_file("config/app.yml",root: :development)# ...and no ENV-vars were provided#=> {secret: "dummy", debug?: false}# ...with ENV {"debug" => "true"}#=> {secret: "dummy", debug?: true}# Loading productionNero.load_file("config/app.yml",root: :production)# ...and no ENV-vars were provided# raises error: key not found: "SECRET" (KeyError)# ...with ENV {"SECRET" => "s3cr3t", "MAX_THREADS" => "3"}#=> {secret: "s3cr3t", max_threads: 3}

Tip

You can also use Nero.config_for(:app) (similar to Rails.application.config_for).
In Rails applications this gets configured for you. For other application you might need to adjust the config_dir:

Nero.configuredo |config|
config.config_dir="config"endNero.config_for(:settings,env: Rails.env)

API Documentation.

built-in tags

The following tags are provided:

  • !env KEY, !env? KEY
    Resp. to fetch or get a value from ENV:
    ---
    # requiredsecret: !env SECRET# optional, with fallback:secret: !env [SECRET, "dummy-fallback"]# ...or nilsecret: !env? SECRET
  • to coerce env-values:
    • env/integer, env/integer?, env/float, env/float?:
      port: !env/integer [PORT, 3000]threads: !env/integer? THREADS # nil when not providedthreshold: !env/float CUTOFF
    • env/bool, env/bool?:
      # required (valid values 'y(es)'/'n(o)', 'true'/'false', 'on'/'off')over18: !env/bool OVER18# optional, with fallback:secure: !env/bool [SECURE, true]# ...or false:debug?: !env/bool? DEBUG

Tip

Make all env-var's optional by providing ENV["NERO_ENV_ALL_OPTIONAL"], e.g.

$ env NERO_ENV_ALL_OPTIONAL=1 SECRET_KEY_BASE_DUMMY=1 rails asset:precompile
  • !path
    Create a Pathname:
    config: !path config# combining tags:asset_folder: !path
    - !envPROJECT_ROOT
    - /public/assets
  • !path/git_root, !path/rails_root
    Create a Pathname relative to some root-path.
    The root-path is expected to be an existing ancestor folder of the yaml-config being parsed.
    It's found by traversing up and checking for the presence of specific files/folders, e.g. '.git' (!path/git_root) or 'config.ru' (!path/rails_root).
    While the root-path needs to exist, the resulting Pathname doesn't need to.
    project_root: !path/git_rootconfig_folder: !path/rails_root [ config ]
  • !uri
    Create a URI:
    smtp_url: !uri
    - smtps://
    - !envSMTP_CREDS- @smtp.gmail.com
  • !str/format
    Using Ruby's format specifications:
    smtp_url: !str/format
    - smtps://%s:%s@smtp.gmail.com
    - !envSMTP_USER
    - !envSMTP_PASS# pass it a map (including a key 'fmt') to use referencessmtp_url: !str/formatfmt: smtps://%<user>s:%<pass>s@smtp.gmail.comuser: !env SMTP_USERpass: !env SMTP_PASS
  • !ref
    Include values from elsewhere:
    # simplemin_threads: !env/integer [MIN_THREADS, !ref [max_threads]]max_threads: 5# oauth_callback -refs-> base.url -refs-> base.hostbase:
    host: !env [HOST]url: !str/format ['https://%s', !ref[base, host]]oauth_callback: !str/format
    - '%s/oauth/callback'
    - !ref[base,url]# refs are resolved within the tree of the selected root.# The following config won't work when doing `Nero.load_file("config/app.yml", root: :prod)`dev:
    max_threads: 5prod:
    max_threads: !env[MAX_THREADS, !ref[dev, max_threads]]
    NOTE future version should raise properly over ref-ing a non-existing path.

custom tags

There's three ways to create your own tags.

For all these methods it's helpful to see the API-docs for Nero::BaseTag.

  1. a proc
    Nero.configuredo |nero|
    nero.add_tag("upcase")do |tag|
    # `tag` is a `Nero::BaseTag`.# In YAML args are provided as scalar, seq or map:# ---# k: !upcase bar# ---# k: !upcase [bar] # equivalent to:# k: !upcase# - bar# ---# k: !upcase# bar: baz## Find these args via `tag.args` (Array or Hash):casetag.argswhenHashtag.args.each_with_object({}){|(k,v),acc| acc[k]=v.upcase}elsetag.args.map(&:upcase)end# NOTE though a tag might just need one argument (ie scalar),# it's helpful to accept a seq as it allows for chaining:# a: !my/inc 4 # scalar suffices# ...but when chaining, it needs to be a seq:# a: !my/inc [ !my/square 2 ]endend
    Blocks are passed instances of Nero::BaseTag.
  2. re-use existing tag-class
    You can add an existing tag under a better fitting name this way.
    Also: some tag-classes have options that allow for simple customizations (like coerce below):
    Nero.configuredo |nero|
    nero.add_tag("env/upcase",klass: Nero::EnvTag[coerce: :upcase])# Alias for path/git_root:nero.add_tag("path/project_root",klass: Nero::PathRootTag[containing: '.git'])end
  3. custom class
    classRotTag < Nero::BaseTag# Configure:# ```# config.add_tag("rot/12", klass: RotTag[n: 12])# config.add_tag("rot/10", klass: RotTag[n: 10]) do |secret|# "#{secret} (try breaking this!)"# end# ```## Usage in YAML:# ```# secret: !rot/12 some message# very_secret: !rot/10 [ !env [ MSG, some message ] ]# ```# => {secret: "EAyq yqEEmsq", very_secret: "Cywo woCCkqo (try breaking this!)"}# By overriding `init_options` we can restrict/require options,# provide default values and do any other setup. # By default an option is available via `options[:foo]`.definit_options(n: 10)super# no specific assignments, so available via `options[:n]`.enddefchars@chars ||= (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a)enddefresolve(**)# currently no keywords are passed, but `**` allows for future ones.# Here we actually do the work: get the args, rotate strings and delegate to the block.# `args` are the resolved nested args (so e.g. `!env MSG` is already resolved).# `config` is the tag's config, and contains e.g. the block.block=config.fetch(:block,:itself.to_proc)# String#tr replaces any character from the first collection with the same position in the other:args.join.tr(chars.join,chars.rotate(options[:n]).join).then(&block)endend

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/eval/nero.

License

The gem is available as open source under the terms of the MIT License.

About

🔥 Declarative YAML-tags

Topics

Resources

Stars

10 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages