{"id":24746490,"url":"https://github.com/eval/nero","last_synced_at":"2025-03-23T01:13:28.015Z","repository":{"id":274447781,"uuid":"922964602","full_name":"eval/nero","owner":"eval","description":"🔥 Declarative YAML-tags","archived":false,"fork":false,"pushed_at":"2025-03-19T20:41:30.000Z","size":93,"stargazers_count":8,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-19T21:39:24.155Z","etag":null,"topics":["rails","ruby","yaml"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eval.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-27T12:10:34.000Z","updated_at":"2025-03-19T17:01:25.000Z","dependencies_parsed_at":"2025-02-11T13:39:30.727Z","dependency_job_id":"5e7b8dd5-fe25-45ef-9883-83e9947c7078","html_url":"https://github.com/eval/nero","commit_stats":null,"previous_names":["eval/nero"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eval%2Fnero","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eval%2Fnero/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eval%2Fnero/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eval%2Fnero/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eval","download_url":"https://codeload.github.com/eval/nero/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244513438,"owners_count":20464596,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["rails","ruby","yaml"],"created_at":"2025-01-28T04:23:43.273Z","updated_at":"2025-03-23T01:13:28.005Z","avatar_url":"https://github.com/eval.png","language":"Ruby","readme":"# 🔥 Nero \n\n[![Gem Version](https://badge.fury.io/rb/nero.svg)](https://badge.fury.io/rb/nero)\n\nNero is a RubyGem that offers declarative YAML-tags to simplify config files, e.g. for requiring and coercion of env-vars.  \nAdditionally, it allows you to create your own.\n\n**Sample:**\n\n```yaml\ndevelopment:\n  # env-var with default value\n  secret: !env [SECRET, \"dummy\"]\n\n  # optional env-var with coercion\n  debug?: !env/bool? DEBUG\n\nproduction:\n  # required env-var (not required during development)\n  secret: !env SECRET\n\n  # coercion\n  max_threads: !env/integer [MAX_THREADS, 5]\n\n  # refer to other keys\n  min_threads: !env/integer [MIN_THREADS, !ref max_threads ]\n\n  # descriptive names\n  asset_folder: !path/rails_root [ public/assets ]\n\n  # easy to add custom tags\n  cache_ttl: !duration [2, hours]\n```\n\n## Highlights\n\n* 💎 declarative YAML-tags for e.g. requiring and coercing env-vars\n* 🛠️ add custom tags\n* 🛤️ `Rails.application.config_for` drop-in\n* ♻️ Zeitwerk-only dependency\n\n## Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n```bash\nbundle add nero\n```\n\n## Configuration\n\n```ruby\nNero.configure do |nero|\n  nero.config_dir = Rails.root / \"config\"\n  # Now `Nero.load_file(:foo)` looks for `Rails.root / \"config/foo.yml\"`.\n\n  # Add custom tags\n  nero.add_tag(\"upcase\") do |tag|\n    # tag is an instance of [Nero::BaseTag](https://rubydoc.info/github/eval/nero/main/Nero/BaseTag)\n  end\nend\n```\n\n## Usage\n\n\u003e [!WARNING]  \n\u003e It's early days - the API and included tags will certainly change. Check the CHANGELOG when upgrading.\n\n### loading a config\n\nGiven the following config:\n```yaml\n# config/settings.yml\ndevelopment:\n  # env-var with a fallback\n  secret: !env [SECRET, \"dummy\"]\n  # Though the default is false, explicitly providing \"false\"/\"off\"/\"n\"/\"no\" also works.\n  debug?: !env/bool? DEBUG\nproduction:\n  # fail-fast on absence of SECRET\n  secret: !env SECRET\n  # always an integer\n  max_threads: !env/integer [MAX_THREADS, 5]\n```\n\nLoading this config:\n\n```ruby\n# Loading development\nNero.load_config(\"config/settings\", root: :development)\n# ...and no ENV-vars were provided\n#=\u003e {secret: \"dummy\", debug?: false}\n\n# ...with ENV {\"debug\" =\u003e \"true\"}\n#=\u003e {secret: \"dummy\", debug?: true}\n\n# Loading production\nNero.load_config(\"config/settings\", root: :production)\n# ...and no ENV-vars were provided\n# raises error: key not found: \"SECRET\" (KeyError)\n\n# ...with ENV {\"SECRET\" =\u003e \"s3cr3t\", \"MAX_THREADS\" =\u003e \"3\"}\n#=\u003e {secret: \"s3cr3t\", max_threads: 3}\n```\n\u003e [!TIP]  \n\u003e The following configuration would make `Nero.load_config` a drop-in replacement for [Rails.application.config_for](https://api.rubyonrails.org/classes/Rails/Application.html#method-i-config_for):\n```ruby\nNero.configure do |config|\n  config.config_dir = Rails.root / \"config\"\nend\n\nNero.load_config(:settings, env: Rails.env)\n```\n\n### built-in tags\n\nThe following tags are provided:\n- `!env KEY`, `!env? KEY`  \n  Resp. to fetch or get a value from `ENV`:\n  ```yaml\n  ---\n  # required\n  secret: !env SECRET\n  # optional, with fallback:\n  secret: !env [SECRET, \"dummy-fallback\"]\n  # ...or nil\n  secret: !env? SECRET\n  ```\n- to coerce env-values:\n  - `env/integer`, `env/integer?`, `env/float`, `env/float?`:  \n    ```yaml\n    port: !env/integer [PORT, 3000]\n    threads: !env/integer? THREADS # nil when not provided\n    threshold: !env/float CUTOFF\n    ```\n  - `env/bool`, `env/bool?`:  \n    ```yaml\n    # required (valid values 'y(es)'/'n(o)', 'true'/'false', 'on'/'off')\n    over18: !env/bool OVER18\n    # optional, with fallback:\n    secure: !env/bool [SECURE, true]\n    # ...or false:\n    debug?: !env/bool? DEBUG\n    ```\n\u003e [!TIP]  \n\u003e Make all env-var's optional by providing `ENV[\"NERO_ENV_ALL_OPTIONAL\"]`, e.g.\n```shell\n$ env NERO_ENV_ALL_OPTIONAL=1 SECRET_KEY_BASE_DUMMY=1 rails asset:precompile\n```\n- `!path`  \n  Create a [Pathname](https://rubyapi.org/3.4/o/pathname):\n  ```yaml\n  config: !path config\n  # combining tags:\n  asset_folder: !path\n    - !env PROJECT_ROOT\n    - /public/assets\n  ```\n- `!path/git_root`, `!path/rails_root`  \n  Create a Pathname relative to some root-path.  \n  The root-path is expected to be an existing ancestor folder of the yaml-config being parsed.  \n  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`).  \n  While the root-path needs to exist, the resulting Pathname doesn't need to.\n  ```yaml\n  project_root: !path/git_root\n  config_folder: !path/rails_root [ config ]\n  ```\n- `!uri`  \n  Create a [URI](https://rubyapi.org/3.4/o/uri):\n  ```yaml\n  smtp_url: !uri\n    - smtps://\n    - !env SMTP_CREDS\n    - @smtp.gmail.com\n  ```\n- `!str/format`  \n  Using Ruby's [format specifications](https://docs.ruby-lang.org/en/master/format_specifications_rdoc.html):\n  ```yaml\n  smtp_url: !str/format\n    - smtps://%s:%s@smtp.gmail.com\n    - !env SMTP_USER\n    - !env SMTP_PASS\n  \n  # pass it a map (including a key 'fmt') to use references\n  smtp_url: !str/format\n    fmt: smtps://%\u003cuser\u003es:%\u003cpass\u003es@smtp.gmail.com\n    user: !env SMTP_USER\n    pass: !env SMTP_PASS\n  ```\n- `!ref`  \n  Include values from elsewhere:\n  ```yaml\n  # simple\n  min_threads: !env/integer [MIN_THREADS, !ref [max_threads]]\n  max_threads: 5\n  \n  # oauth_callback -refs-\u003e base.url -refs-\u003e base.host\n  base:\n    host: !env [HOST]\n    url: !str/format ['https://%s', !ref[base, host]]\n  oauth_callback: !str/format\n    - '%s/oauth/callback'\n    - !ref[base, url]\n\n  # refs are resolved within the tree of the selected root.\n  # The following config won't work when doing `Nero.load_config(:app, root: :prod)`\n  dev:\n    max_threads: 5\n  prod:\n    max_threads: !env[MAX_THREADS, !ref[dev, max_threads]]\n  ```\n  NOTE future version should raise properly over ref-ing a non-existing path.\n\n### custom tags\n\nThree ways to do this:\n\n1. a block\n    ```ruby\n    Nero.configure do |nero|\n      nero.add_tag(\"upcase\") do |tag|\n        # `tag` is a `Nero::BaseTag`.\n        # In YAML args are provided as scalar, seq or map:\n        # ---\n        # k: !upcase bar\n        # ---\n        # k: !upcase [bar] # equivalent to:\n        # k: !upcase\n        #   - bar\n        # ---\n        # k: !upcase\n        #   bar: baz\n        #\n        # Find these args via `tag.args` (Array or Hash):\n        case tag.args\n        when Hash\n          tag.args.each_with_object({}) {|(k,v), acc| acc[k] = v.upcase }\n        else\n          tag.args.map(\u0026:upcase)\n        end\n\n        # NOTE though a tag might just need one argument (ie scalar),\n        # it's helpful to accept a seq as it allows for chaining:\n        # a: !my/inc 4 # scalar suffices\n        # ...but when chaining, it needs to be a seq:\n        # a: !my/inc [ !my/square 2 ]\n      end\n    end\n    ```\n    Blocks are passed instances of [Nero::BaseTag](https://rubydoc.info/github/eval/nero/main/Nero/BaseTag).\n1. re-use existing tag-class  \n   You can add an existing tag under a better fitting name this way.  \n   Also: some tag-classes have options that allow for simple customizations (like `coerce` below):\n    ```ruby\n    Nero.configure do |nero|\n      nero.add_tag(\"env/upcase\", klass: Nero::EnvTag[coerce: :upcase])\n\n      # Alias for path/git_root:\n      nero.add_tag(\"path/project_root\", klass: Nero::PathRootTag[containing: '.git'])\n    end\n    ```\n1. custom class  \n   ```ruby\n   class RotTag \u003c Nero::BaseTag\n     # Configure:\n     # ```\n     # config.add_tag(\"rot/12\", klass: RotTag[n: 12])\n     # config.add_tag(\"rot/10\", klass: RotTag[n: 10]) do |secret|\n     #   \"#{secret} (try breaking this!)\"\n     # end\n     # ```\n     #\n     # Usage in YAML:\n     # ```\n     # secret: !rot/12 some message\n     # very_secret: !rot/10 [ !env [ MSG, some message ] ]\n     # ```\n     # =\u003e {secret: \"EAyq yqEEmsq\", very_secret: \"Cywo woCCkqo (try breaking this!)\"}\n   \n     # By overriding `init_options` we can restrict/require options,\n     # provide default values and do any other setup.  \n     # By default an option is available via `options[:foo]`.\n     def init_options(n: 10)\n       super # no specific assignments, so available via `options[:n]`.\n     end\n\n     def chars\n       @chars ||= (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a)\n     end\n\n     def resolve(**) # currently no keywords are passed, but `**` allows for future ones.\n       # Here we actually do the work: get the args, rotate strings and delegate to the block.\n       # `args` are the resolved nested args (so e.g. `!env MSG` is already resolved).\n       # `config` is the tag's config, and contains e.g. the block.\n       block = config.fetch(:block, :itself.to_proc)\n       # String#tr replaces any character from the first collection with the same position in the other:\n       args.join.tr(chars.join, chars.rotate(options[:n]).join).then(\u0026block)\n     end\n   end\n   ```\n\n## Development\n\nAfter 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.\n\nTo 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](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/eval/nero.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feval%2Fnero","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feval%2Fnero","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feval%2Fnero/lists"}