Giter Club home page Giter Club logo

anyway_config's Introduction

Hey / Привет 👋

My name is Vladimir (or Вова /ˈvo.və/), and I'm a principal backend engineer at @evilmartians.

📕 My book "Layered design for Ruby on Rails applications" is avalable now: Amazon | Packt

I'm working on:

Check out some of my blog posts:

...and conference talks:

anyway_config's People

Contributors

aderyabin avatar bessey avatar carlomartinucci avatar carlqt avatar charlie-wasp avatar depfu[bot] avatar drnic avatar dsalahutdinov avatar envek avatar fargelus avatar fractaledmind avatar inner-whisper avatar jastkand avatar jtuttle avatar lokideos avatar marshall-lee avatar micahlee avatar mt-kelvintaywl avatar onemanstartup avatar palkan avatar petergoldstein avatar prog-supdex avatar progapandist avatar sandrods avatar skryukov avatar sponomarev avatar stiig avatar storkvist avatar tagirahmad avatar viktorchukhantsev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

anyway_config's Issues

Warnings about deprecation of using the last argument as keyword arguments in Ruby 2.7

What did you do?

Used gem with Ruby 2.7.

What did you expect to happen?

Everything should work.

What actually happened?

Everything worked as expected but I saw warnings about the deprecation of using the last argument as keyword arguments.

Additional context

/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:30: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:36: warning: The called method `load_from_file' is defined here
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:31: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:47: warning: The called method `load_from_secrets' is defined here
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:32: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:53: warning: The called method `load_from_credentials' is defined here
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/rails/config.rb:33: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
/Users/storkvist/.asdf/installs/ruby/2.7.0/lib/ruby/gems/2.7.0/gems/anyway_config-2.0.0.pre2/lib/anyway/config.rb:188: warning: The called method `load_from_env' is defined here

Environment

Ruby Version: 2.7

Framework Version (Rails, whatever): 6.0.2.1

Anyway Config Version: 2.0.0.pre2

Automatic predicate methods for coerced booleans

Given a config with a coerced boolean, it would be really nice to have predicate methods generated automatically. I'm thinking this is something that you might want to disable rather than opt-in to, but I'd be fine with either way.

class MyConfig < Anyway::Config
  attr_config enabled: "true"
  coerce_boolean enabled: :boolean
end

MyConfig.new.enabled? #=> true

# config
config.anyway_config.predicates_for_coerced_booleans = [false | true]

Can't use with Rails Credentials

I created a brand new application using Rails 5. To my config/application.rb I added the following code:

# config/application.rb
module Poloris
  class Config < Anyway::Config
    attr_config(
      database_host: 'localhost',
      database_port: 5432,
      database_user: 'root',
      database_password: 'debiru'
    )
  end

  def self.config
    @config ||= Config.new
  end
  class Application < Rails::Application
    ...
  end
end

in my credentials I added:

poloris:
  secret_key_base: 93d324c53f5cdd193349aec6e97191d502d34ecedbb44cac1aac51ee62dd2ca18d3c18b110781bdb6416c6ae1acdde1a49600cb5d4bdd66a5277eabcb7f9ff05
  database_host: 'localhost'
  database_port: 5432
  database_user: 'debiru'
  database_password: 'debiru'

but when I try doing AnywayConfig.for(:poloris) I get:

irb(main):001:0> Anyway::Config.for(:poloris)
Traceback (most recent call last):
        1: from (irb):1
TypeError (no implicit conversion of nil into Hash)

What am I doing wrong?

Writer receives JSON from environment variable as array of strings, not a string

What did you do?

I have a .env file that has JSON in it:

FOO_BAR='{"baz":"fizz","baz2":"fizz2","baz3":"fizz3"}'

And a config:

class Config < Anyway::Config
  attr_config :bar
  env_prefix :foo
  def bar=(val)
    super JSON.parse(val)
  end
end

What did you expect to happen?

I expected that config.bar would be a hash {baz: "fizz", baz2: "fizz2", baz3: "fizz3"}

What actually happened?

I've got an error no implicit conversion of Array into String.

Additional context

When I've put puts val into writer, it printed something like

{"baz":  "fizz"
baz2":  "fizz2
"baz3":  "fizz3"}

Actually I've got the whole thing working with

  def bar=(val)
    super JSON.parse(val.join('","').gsub('""', '"'))
  end

But that looks aweful.

Environment

Ruby Version:
ruby 2.7.1p83

Framework Version (Rails, whatever):
none (custom)

Anyway Config Version:
anyway_config (2.0.5)

Allow the (undocumented) setting `.default_config_path` to accept just a directory name instead of a callable

Is your feature request related to a problem? Please describe.

It was not immediately obvious to me that .default_config_path needs a callable. I just happened to come across this setting in the code; it does not seem to exist in the readme (the readme only mentions the environment override).

Describe the solution you'd like

I propose that this setting be converted to just represent the directory containing the config files and that the .yml file extension be assumed.
Alternately, the setting can be either a string (or Pathname) or a lambda, and the lambda be used only if the setting responds to .call. This also takes care of the use case where a user might want a file extension other than .yml.

Additionally, this setting should be documented in the readme.

Describe alternatives you've considered

Setting the attribute to be a lambda works just fine, but it feels very implementation reliant.

Additional context

I would be happy to create a pull request with the implementation for this.

Depfu Error: Depfu is stuck and needs your help

Hello,

⚠️ We're getting errors with this repo and have given up after trying several times.

In most cases that means something is wrong with your current Bundler setup and we can't fix it automatically:

• Error details:
Bundler Error: 
[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
You specified: ruby-next (>= 0.5.1) and ruby-next (>= 0.5). Bundler cannot continue.

 #  from /tmp/d20200430-4-1xpgxmp/Gemfile:24
 #  -------------------------------------------
 #  gem 'rspec', '~> 3.8'
 >  gem 'ruby-next', '>= 0.5'
 #  -------------------------------------------

• Error details:
Bundler Error: 
[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
You specified: ruby-next (>= 0.5.1) and ruby-next (>= 0.5). Bundler cannot continue.

 #  from /tmp/d20200424-4-nl9us2/Gemfile:24
 #  -------------------------------------------
 #  gem 'rspec', '~> 3.8'
 >  gem 'ruby-next', '>= 0.5'
 #  -------------------------------------------

• Error details:
Bundler Error: 
[!] There was an error parsing `Gemfile`: You cannot specify the same gem twice with different version requirements.
You specified: ruby-next-core (>= 0.5.1) and ruby-next-core (= 0.5.3). Bundler cannot continue.

 #  from /tmp/d20200326-4-1iv2kef/Gemfile:19
 #  -------------------------------------------
 #  
 >  gem 'ruby-next-core', '= 0.5.3'
 #  gem 'ammeter', '~> 1.1.3'
 #  -------------------------------------------

After you've fixed the problem, please activate this project again in the Depfu Dashboard.

👉 We will not send you further PRs until this is fixed and the repo is activated again.

If you need help or this looks like an error on our side, please send us an email.

Unable to exclude environments in Rails configuration file

What did you do?

The README says you can't mix-and-match environment and non-environment values in the config file but it seems like I should be able to have one or the other.

In a Rails app, I tried to use a YAML configuration file without specifying any environments:

my_config.rb

class MyConfig < Anyway::Config
  attr_config :foo
end

my.yml

foo: bar

What did you expect to happen?

I expected MyConfig to read the value of foo as "bar".

What actually happened?

The MyConfig object is unable to read the value of foo from the YAML file:

[1] pry(main)> require_relative './lib/config/my_config.rb'
=> true
[2] pry(main)> MyConfig.new.foo
=> nil

Additional context

I'm able to load the value just fine if I include the Rails env in the config file, so I know it's not a mistake setting up paths or anything like that. If I change the config file as follows it works:

my.yml

development:
  foo: bar
[1] pry(main)> require_relative './lib/conjur/my_config.rb'
=> true
[2] pry(main)> MyConfig.new.foo
=> "bar"

but for this particular use case I don't want to have to specify environments in the config file.

Environment

Ruby Version: Ruby 2.5.8p224

Framework Version (Rails, whatever): Rails 5.2.6

Anyway Config Version: 2.1.0

Config attribute not read from environment variables command line

What did you do?

$ DIANPING_SITE=http://localhost rails c
Running via Spring preloader in process 21932
Loading development environment (Rails 6.0.3.4)
irb: warn: can't alias context from irb_context.
2.7.2 :001 > ENV["DIANPING_SITE"]
 => "http://localhost" 
2.7.2 :002 > DianpingConfig.site
 => nil 
2.7.2 :003 > DianpingConfig.env_prefix
 => "DIANPING" 
2.7.2 :009 > Anyway::VERSION
 => "2.1.0" 

What did you expect to happen?

Get Value ENV_CONF=value

What actually happened?

return nil

Additional context

Environment

Ruby Version:
ruby 2.7.2 MRI
Framework Version (Rails, whatever):
rails 6.0.3.4
Anyway Config Version:
2.1.0

Configs are not loaded in production environment

What did you do?

Run Rails and Sidekiq in production.

What did you expect to happen?

Eager loaded config classes.

What actually happened?

Classes are not loaded, Sidekiq jobs that use configs throw exceptions, Rails console in Sidekiq environment does not have classes loaded.

Additional context

This workaround helped:

config.eager_load_paths << Rails.root.join("config", "configs")

Environment

production

Ruby Version:
2.6.0

Framework Version (Rails, whatever):
Rails 5.2.4.2

Anyway Config Version:
2.0.1

v2.0 roadmap

Most of functionality has been covered by #24 and released as 2.0.0.pre.

The goal of v2.0 is to refactor the codebase (make it more maintainable) and add a bunch of new features:

  • Introduce better code style rules (added standard)
  • Split God-like Config class into modules
  • Fix configs inheritance
  • Improve naming inference to support <Some>Config (for app-level configs)
  • Refactor sources mechanism and introduce loaders API (#46)
  • Replace Config.new(overrides: data) with Config.new(data) (breaking)
  • Better Rails integration (global config, app/configs support)
  • Credentials support (with per-env creds)
  • *.yml.local and .enc.local support
  • Source tracing (which source did this value come from?) (#53)
  • Testing utils (with_env({key => val}){ ... })
  • Add Rails generator rails g config name key,key2,key3 (#52)
  • Required parameters validation (#50)
  • Validate usage of reserved attribute names (#50)

Minor TODOs:

  • Support source tracing in OptParser
  • Add #deconstruct_keys
  • Add on_load hook

Local files stoped working after upgrading from 2.0.0.pre2 to 2.0.0.rc1

What did you do?

Upgraded gem from 2.0.0.pre2 to 2.0.0.rc1.

What did you expect to happen?

Everything will work as expected.

What actually happened?

Local files stopped working.

Additional context

# config/development.rb
# ...
Anyway::Settings.use_local_files = true

Environment

Ruby Version:

2.7.0-p0

Framework Version (Rails, whatever):

Rails 6.0.2.2

Anyway Config Version:

2.0.0.rc1

Truffleruby Compatibility

$ require 'anyway_config'

<internal:core> core/kernel.rb:226:in `gem_original_require': /Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/gems/gems/anyway_config-2.3.0/lib/.rbnext/3.1/anyway/tracing.rb:10: syntax error, unexpected '=' (SyntaxError)
	from <internal:/Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/mri/rubygems/core_ext/kernel_require.rb>:96:in `require'
	from /Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/gems/gems/anyway_config-2.3.0/lib/anyway_config.rb:17:in `<top (required)>'
	from <internal:core> core/kernel.rb:234:in `gem_original_require'
	from <internal:/Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/mri/rubygems/core_ext/kernel_require.rb>:160:in `require'
	from (irb):1:in `<top (required)>'
	from <internal:core> core/kernel.rb:407:in `loop'
	from <internal:core> core/throw_catch.rb:36:in `catch'
	from <internal:core> core/throw_catch.rb:36:in `catch'
	from /Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/gems/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
	from <internal:core> core/kernel.rb:376:in `load'
	from <internal:core> core/kernel.rb:376:in `load'
	from /Users/ollym/.rbenv/versions/truffleruby-22.2.0/bin/irb:42:in `<main>'
<internal:core> core/kernel.rb:236:in `gem_original_require': cannot load such file -- anyway_config (LoadError)
	from <internal:/Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/mri/rubygems/core_ext/kernel_require.rb>:85:in `require'
	from (irb):1:in `<top (required)>'
	from <internal:core> core/kernel.rb:407:in `loop'
	from <internal:core> core/throw_catch.rb:36:in `catch'
	from <internal:core> core/throw_catch.rb:36:in `catch'
	from /Users/ollym/.rbenv/versions/truffleruby-22.2.0/lib/gems/gems/irb-1.3.5/exe/irb:11:in `<top (required)>'
	from <internal:core> core/kernel.rb:376:in `load'
	from <internal:core> core/kernel.rb:376:in `load'
	from /Users/ollym/.rbenv/versions/truffleruby-22.2.0/bin/irb:42:in `<main>'

Possible 2.0.0 regression with isolator

What did you do?

Updated anyway_config from 1.4.4 -> 2.0.0

What did you expect to happen?

My CI suite to run!

What actually happened?

It crashes on suite start when I try to require isolator:


An error occurred while loading spec_helper.
--
474 | Failure/Error: require "isolator"
474 |  
475 | FrozenError:
477 | can't modify frozen Array
477 | # ./spec/spec_helper.rb:26:in `<main>'

Additional context

Our Gemfile:

gem "isolator", require: false

isolator 0.6.2
sniffer 0.4.0
anyway_config 2.0.0

Environment

Ruby Version:

2.6.6

Framework Version (Rails, whatever):

Rails 6.0.2.2
RSpec 3.9.0

Anyway Config Version:

Default config_name of classes with multiple words ought be underscore-separated

What did you do?

  1. Made a config called GoodJobConfig
  2. required some attributes in that config
  3. Provided a default in config/good_job.yml

What did you expect to happen?

App starts, defaults loaded

What actually happened?

AnywayConfig looks for configurations for goodjob, fails to load the yml file contents, spits out the following error.

Uncaught exception: The following config parameters for `GoodJobConfig(config_name: goodjob)` are missing or empty: max_threads, execution_mode, enable_cron
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:455:in `raise_validation_error'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:442:in `block in validate_required_attributes!'
	<internal:kernel>:124:in `then'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:440:in `validate_required_attributes!'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:78:in `apply_to'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:377:in `block in load'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:377:in `each'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:377:in `load'
	/Users/dazmin/.gem/ruby/3.1.2/gems/anyway_config-2.3.0/lib/anyway/config.rb:337:in `initialize'
	/Users/dazmin/mmx/config/configs/application_config.rb:14:in `new'
	/Users/dazmin/mmx/config/configs/application_config.rb:14:in `instance'
	/Users/dazmin/.gem/ruby/3.1.2/gems/activesupport-7.0.3.1/lib/active_support/core_ext/module/delegation.rb:303:in `method_missing'
	/Users/dazmin/mmx/config/application.rb:39:in `<class:Application>'
	/Users/dazmin/mmx/config/application.rb:11:in `<module:Mmx>'
	/Users/dazmin/mmx/config/application.rb:9:in `<top (required)>'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/commands/server/server_command.rb:137:in `require'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/commands/server/server_command.rb:137:in `block in perform'
	<internal:kernel>:90:in `tap'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/commands/server/server_command.rb:134:in `perform'
	/Users/dazmin/.gem/ruby/3.1.2/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
	/Users/dazmin/.gem/ruby/3.1.2/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
	/Users/dazmin/.gem/ruby/3.1.2/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/command/base.rb:87:in `perform'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/command.rb:48:in `invoke'
	/Users/dazmin/.gem/ruby/3.1.2/gems/railties-7.0.3.1/lib/rails/commands.rb:18:in `<top (required)>'
	/Users/dazmin/mmx/bin/rails:4:in `require'
	/Users/dazmin/mmx/bin/rails:4:in `<top (required)>'
	/Users/dazmin/.gem/ruby/3.1.2/gems/debase-3.0.0.beta.4/lib/debase.rb:211:in `load_protect'
	/Users/dazmin/.gem/ruby/3.1.2/gems/debase-3.0.0.beta.4/lib/debase.rb:211:in `debug_load'

Process finished with exit code 1

Additional context

I was able to work around this error by setting config_name :good_job.

Environment

Ruby Version:
3.1.2

Framework Version (Rails, whatever):
Rails 7.0.3.1

Anyway Config Version:
2.3.0

Psych 4: safe_load by default, Ruby 3.1 - Unknown alias: default

What did you do?

A rather basic newly created Rails app with --main flag, so reports itself as 7.1.0.alpha.
Seems to happen even if I downgrade to Rails 7.0.

If I try to start AnyCable with bundle exec anycable, I get the following error:

Unknown alias: default

The error seems to come from Anyway Config, so creating an issue here.
Seems to happen because of Psych 4.
Related information:
https://www.redmine.org/issues/36226

What did you expect to happen?

I expected AnyCable to start.

The basic AnyCable config uses a default setting, which is not recognized.

If I change:

# anyway_config-2.2.2/lib/anyway/loaders/yaml.rb
# line 32
if defined?(ERB)
  ::YAML.safe_load(ERB.new(File.read(path)).result) || {} # rubocop:disable Security/YAMLLoad
else
...

to using unsafe load, the config works fine. Also if I remove the default block and just copy & paste the settings everything also works fine.

What actually happened?

I get the following error:

Unknown alias: default
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:430:in `visit_Psych_Nodes_Alias'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:30:in `visit'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:6:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:35:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:345:in `block in revive_hash'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `each'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `each_slice'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `revive_hash'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:167:in `visit_Psych_Nodes_Mapping'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:30:in `visit'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:6:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:35:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:345:in `block in revive_hash'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `each'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `each_slice'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:343:in `revive_hash'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:167:in `visit_Psych_Nodes_Mapping'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:30:in `visit'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:6:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:35:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:318:in `visit_Psych_Nodes_Document'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:30:in `visit'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/visitor.rb:6:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych/visitors/to_ruby.rb:35:in `accept'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych.rb:335:in `safe_load'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/3.1.0/psych.rb:370:in `load'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/anyway_config-2.2.2/lib/anyway/loaders/yaml.rb:33:in `parse_yml'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/anyway_config-2.2.2/lib/anyway/rails/loaders/yaml.rb:8:in `load_base_yml'
/home/sampo/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/anyway_config-2.2.2/lib/anyway/loaders/yaml.rb:13:in `block in call'

Additional context

Environment

Development

Ruby Version:

3.1.0

Framework Version (Rails, whatever):

Rails from main branch, 7.1.0.alpha

Anyway Config Version:
2.2.2

Zeitwerk::Error when using autoload_static_config_path = "config/configs"

What did you do?

I run the following:

rails new anyway_config -d postgresql
bundle add anyway_config
rails g anyway:install --configs-path=config/configs

then I uncommented config.anyway_config.autoload_static_config_path = "config/configs" in config/application.rb file and executed:

rails g anyway:config database username password

but it fails on the aforementioned uncommented line of code upon any rails process (console, server). It does work with app/config.

What did you expect to happen?

I expected it not to fail.

What actually happened?

I got the following Zeitwerk error.

$ rails g anyway:config database username password                                                            
/Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:471:in `block (3 levels) in raise_if_conflicting_directory': loader (Zeitwerk::Error)

#<Zeitwerk::Loader:0x00000001103de120
 @autoloaded_dirs=[],
 @autoloads={},
 @collapse_dirs=#<Set: {}>,
 @collapse_glob_patterns=#<Set: {}>,
 @eager_load_exclusions=#<Set: {}>,
 @eager_loaded=false,
 @ignored_glob_patterns=#<Set: {}>,
 @ignored_paths=#<Set: {}>,
 @inflector=Rails::Autoloaders::Inflector,
 @initialized_at=2023-01-16 16:23:48.376379 +0100,
 @logger=nil,
 @mutex=#<Thread::Mutex:0x00000001121ca4b8>,
 @mutex2=#<Thread::Mutex:0x00000001121ca490>,
 @namespace_dirs={},
 @on_load_callbacks={},
 @on_setup_callbacks=[],
 @on_unload_callbacks={},
 @reloading_enabled=false,
 @roots={},
 @setup=false,
 @shadowed_files=#<Set: {}>,
 @tag="anyway.config",
 @to_unload={}>
wants to manage directory /Users/me/ws/tzif.io/hix/try/anyway_config/config/configs, which is already managed by

#<Zeitwerk::Loader:0x00000001103de260
 @autoloaded_dirs=[],
 @autoloads={},
 @collapse_dirs=#<Set: {}>,
 @collapse_glob_patterns=#<Set: {}>,
 @eager_load_exclusions=#<Set: {}>,
 @eager_loaded=false,
 @ignored_glob_patterns=#<Set: {}>,
 @ignored_paths=#<Set: {}>,
 @inflector=Rails::Autoloaders::Inflector,
 @initialized_at=2023-01-16 16:23:48.375365 +0100,
 @logger=nil,
 @mutex=#<Thread::Mutex:0x00000001124153a8>,
 @mutex2=#<Thread::Mutex:0x0000000112415380>,
 @namespace_dirs={},
 @on_load_callbacks={},
 @on_setup_callbacks=[],
 @on_unload_callbacks={},
 @reloading_enabled=false,
 @roots={"/Users/me/ws/tzif.io/hix/try/anyway_config/config/configs"=>Object},
 @setup=false,
 @shadowed_files=#<Set: {}>,
 @tag="anyway.config",
 @to_unload={}>


	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:465:in `each_key'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:465:in `block (2 levels) in raise_if_conflicting_directory'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:461:in `each'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:461:in `block in raise_if_conflicting_directory'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:458:in `synchronize'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader.rb:458:in `raise_if_conflicting_directory'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/zeitwerk-2.6.6/lib/zeitwerk/loader/config.rb:123:in `push_dir'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/anyway_config-2.3.0/lib/anyway/rails/settings.rb:30:in `block in autoload_static_config_path='
	from <internal:kernel>:90:in `tap'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/anyway_config-2.3.0/lib/anyway/rails/settings.rb:27:in `autoload_static_config_path='
	from /Users/me/ws/tzif.io/hix/try/anyway_config/config/application.rb:13:in `<class:Application>'
	from /Users/me/ws/tzif.io/hix/try/anyway_config/config/application.rb:10:in `<module:AnywayConfig>'
	from /Users/me/ws/tzif.io/hix/try/anyway_config/config/application.rb:9:in `<main>'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.15.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.15.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/command/actions.rb:22:in `require_application!'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/command/actions.rb:14:in `require_application_and_environment!'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/commands/generate/generate_command.rb:21:in `perform'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor/command.rb:27:in `run'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor/invocation.rb:127:in `invoke_command'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/thor-1.2.1/lib/thor.rb:392:in `dispatch'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/command/base.rb:87:in `perform'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/command.rb:48:in `invoke'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/railties-7.0.4/lib/rails/commands.rb:18:in `<main>'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.15.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from /Users/me/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/bootsnap-1.15.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
	from bin/rails:4:in `<main>'

Additional context

none

Environment

Ruby Version: 3.2.0

Framework Version (Rails, whatever): 7.0.4

Anyway Config Version: 2.3.0

Using bundle exec puma instead of rails server fails to load Railtie

At this line the Railtie is conditionally loaded depending on whether or not ::Rails::VERSION is defined:

require "anyway/rails" if defined?(::Rails::VERSION)

However, when a Rails app used bundle exec puma to start its webserver instead of bundle exec rails server, ::Rails is not defined at the point that this code is called (in my case I have an /initializers/anyway_config.rb file that is doing minimal configuration, like adding "staging" to known_environments. And I get this error:

 Unable to load application: NoMethodError: undefined method `anyway_config' for #<Rails::Application::Configuration:0x00007fc384baef60>

I have manually debugged and confirmed that the problem is that ::Rails is not defined and that if I run rails server it is defined.

For now, I have worked around this by simply manually calling require "anyway/rails" in my initializer, but this doesn't feel right.

Issue when overriding configs with ActiveRecord class

What did you do?

class MyConfig < Anyway::Config
    attr_config some_class: ActiveRecordModel
end

What did you expect to happen?

> MyConfig.new.some_class.table_name
=> "active_record_models"

> MyConfig.new(some_class: OtherModel).some_class.table_name
=> "other_models"

What actually happened?

> MyConfig.new.some_class.table_name
=> "active_record_models"

> MyConfig.new(some_class: OtherModel).some_class.table_name
=> ""

Additional context

Looks like issue happens when dup method is called on ActiveRecord class before data was loaded to instance variables/etc.
https://github.com/rails/rails/blob/5f3ff60084ab5d5921ca3499814e4697f8350ee7/activerecord/lib/active_record/model_schema.rb#L205

E.g. in development environment:

> OtherModel.dup.table_name
=> ""

> OtherModel.table_name
=> "other_models"

> OtherModel.dup.table_name
=> "other_models"

Ruby Version:
2.6.5

Framework Version (Rails, whatever):
6.0.3

Anyway Config Version:
2.1.0

There is no issue with that on version 2.0.6

Question: correct syntax for `required` complex config attributes?

What did you do?

Given a config class like this:

# frozen_string_literal: true

require 'anyway_config'

module LdapSync
  class Config < Anyway::Config
    config_name :ldap_sync
    env_prefix :ls
    attr_config ldap: {
                  base_dn: nil,
                  user_dn: nil,
                  bind_dn: nil,
                  bind_pw: nil,
                  host: nil,
                  port: 636,
                  tls: true,
                  tls_verify_cert: true,
                  tls_ca_cert_file: '/etc/ssl/certs/ca-certificates.crt',
                  tls_version: 'TLSv1_2'
                },
                database: {
                  host: nil,
                  name: nil,
                  user: nil,
                  password: nil,
                  port: 5432
                }

    coerce_types ldap: { bind_dn: :string }
    # required ...?
  end
end

What is the correct syntax to specify required config attributes like ldap.base_dn, ldap.host or database.host? I cannot seem to get it right.

I have tried:

required :ldap[:base_dn]

Error:

config.rb:31:in `[]': no implicit conversion of Symbol into Integer (TypeError)

Then:

required :ldap['base_dn']

Which doesn't anger Ruby but seems to resolve to nil:

The following config parameters for `LdapSync::Config(config_name: times_sync_ldap_group)` are missing or empty:

And lastly:

required ldap['base_dn']

Which Ruby doesn't like:

config.rb:31:in `<class:Config>': undefined local variable or method `ldap' for LdapSync::Config:Class (NameError)

I am out of ideas and would be thankful for some insight. Is this even possible?

I haven't used anyway config before but I really appreciate all the work it takes off my hands, since before I've been handling YAML-based config settings (loading, reading, validating, etc.) pretty much manually and not in an efficient way like anyway config does. Thanks for creating it!

Ruby Version: 2.7.6

Framework Version (Rails, whatever): none, pure Ruby

Anyway Config Version: 2.3.1

Unexpected error: 'Gem's lib is not in the $LOAD_PATH: /var/task/vendor/bundle/ruby/2.7.0/gems/anyway_config-2.0.6/lib'

Hi @palkan , sorry for the ping but I was wondering if you can point me to a right way to resolve this? 🙏 thank you so much!

What did you do?

I am using anyway_config gem as a dependency in an AWS Lambda function.
For context, the Ruby runtime env for the Lambda is Ruby 2.7

My Gemfile includes

gem 'anyway_config', '2.0.6'
gem 'ruby-next-core', '>= 0.8.0'

I am not sure why when the Lambda is invoked, i am seeing error from Ruby-next from Anyway Config?

{
    "errorMessage": "Gem's lib is not in the $LOAD_PATH: /var/task/vendor/bundle/ruby/2.7.0/gems/anyway_config-2.0.6/lib",
    "errorType": "Init<RuntimeError>",
    "stackTrace": [
        "/var/task/vendor/bundle/ruby/2.7.0/gems/ruby-next-core-0.10.5/lib/ruby-next/language/setup.rb:53:in `setup_gem_load_path'",
        "/var/task/vendor/bundle/ruby/2.7.0/gems/anyway_config-2.0.6/lib/anyway_config.rb:6:in `<top (required)>'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'",
        "/var/task/lib/client/directory.rb:4:in `<top (required)>'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'",
        "/var/task/lib/signals/handler.rb:10:in `<top (required)>'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'",
        "/var/lang/lib/ruby/site_ruby/2.7.0/rubygems/core_ext/kernel_require.rb:72:in `require'"
    ]
}

What did you expect to happen?

the function should have executed successfully without this error.
Forgive me for being ignorant, but I am also curious why the ruby-next lib is needed if the codebase using anyway_config is >= 2.7 ?

What actually happened?

Additional context

Environment

Ruby Version:

2.7.2

Framework Version (Rails, whatever):

Anyway Config Version:

2.0.6

Unset Array type resolves to nil

What did you do?

Given this config:

class AppConfig < Anyway::Config
  attr_config(
    :extra_valid_hosts,
  )

  coerce_types(
    extra_valid_hosts:  { type: :string, array: true },
  )
end

And nothing setting the value of APP_EXTRA_VALID_HOSTS...

What did you expect to happen?

I expect AppConfig.new.extra_valid_hosts to return [] because I have explicitly told AnywayConfig that its an array type.

[3] pry(main)> ENV["APP_EXTRA_VALID_HOSTS"] = ","
=> ","
[6] pry(main)> AppConfig.new.extra_valid_hosts
=> []
[7] pry(main)> ENV["APP_EXTRA_VALID_HOSTS"] = nil
=> nil
[9] pry(main)> AppConfig.new.extra_valid_hosts
=> [] # <<<<<<<< Good

What actually happened?

It returns nil

[3] pry(main)> ENV["APP_EXTRA_VALID_HOSTS"] = ","
=> ","
[6] pry(main)> AppConfig.new.extra_valid_hosts
=> []
[7] pry(main)> ENV["APP_EXTRA_VALID_HOSTS"] = nil
=> nil
[9] pry(main)> AppConfig.new.extra_valid_hosts
=> nil # <<<<<<<< Bad

Environment

Ruby Version: 3.1

Framework Version (Rails, whatever): Rails 6.1

Anyway Config Version: 2.3.1

Can you release a new gem with the latest fixes?

What did you do?

I tried to pull anyway_config gem from the repo and had the old release.

What did you expect to happen?

I wanted the latest patches to fix the gem usage with Rails 7.

What actually happened?

The old gem was installed.

Additional context

Environment

Ruby Version:. 2.7

Framework Version (Rails, whatever): Rails 7

Anyway Config Version: latest released.

Error when no hargs

class MyConfig < Anyway::Config
  attr_config :user, :pass
end

#=> /anyway/config.rb:7:in `dup': can't dup Symbol (TypeError)

Dynamic Loader Per Call

Is your feature request related to a problem? Please describe.

I'd like to have custom loader that will load config from database. However, this can't be cached because this is multi-tenant system where configuration will change from request to request.

Describe the solution you'd like

Would it be possible to provide custom loader which will be called every time config value is requested.

Describe alternatives you've considered

Reload the whole config each request.

Upgrade to v2 messes up Forest Admin schema

What did you do?

I use both isolator (which depends on sniffer which depends on anyway_config) and forest-rails in my app. Forest Admin is a service that provides an admin panel.

I upgraded anyway_config to 2.0.6 and it messed up the .forestadmin-schema.json file. This file is generated by Forest Admin upon starting the Rails server and it contains a description of the database schema based on the ActiveRecord models in the app.

What did you expect to happen?

The .forestadmin-schema.json file to be untouched by the upgrade of the anyway_config gem, as they are unrelated. anyway_config does not appear in the dependency tree of forest-rails.

What actually happened?

After the anyway_config upgrade to 2.0.6, all ActiveRecord models disappeared from the .forestadmin-schema.json file. It's like forest-rails couldn't find any ActiveRecord models anymore.

Additional context

I'm using forest-rails 5.2.1.

Environment

Ruby Version:
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c)

Framework Version (Rails, whatever):
Rails 6.0.3.2

Anyway Config Version:
I upgraded from 1.4.4 to 2.0.6

Environment variable lists with zero / one element are parsed as strings

What did you do?

When loading a config value that's supposed to be a list through an environment variable, the type of the parsed variable differs based on the number of elements in the list (zero/one vs many). That is:

MY_LIST=one is parsed as a string
MY_LIST=one,two,three is parsed as a list

This can be tricky to handle in the code that consumes the configuration value. For example, if trying to write a validation in the on_load method of a configuration class, you have to make sure it handles both types appropriately (or converts one to the other).

What did you expect to happen?

An environment variable with a comma-separated list is parsed as a string.

What actually happened?

An environment variable with a comma-separated list is parsed as a list.

Additional context

Environment

Ruby Version: Ruby 2.5.8p224

Framework Version (Rails, whatever): Rails 5.2.6

Anyway Config Version: 2.1.0

Config to ENV conversion

Is your feature request related to a problem? Please describe.

Make it possible to convert a config instance to the ENV-like hash (which could be merged into ENV or dumped into a file or whatever).

Describe the solution you'd like

Example:

class MyConfig < Anyway::Config
  env_prefix :myconf
  attr_config :host, :port, :user
end

MyConfig.new(host: "localhost", port: 8000, user: {name: "Jack"}).as_env 
#=> 
# {
#   "MYCONF_HOST" => "localhost",
#   "MYCONF_PORT" => "8000",
#   "MYCONF_USER__NAME => "Jack"
# }

Additional context

Based on the discussion: #116

Hash#deep_merge! breaks Rails initialization

What did you do?

Tried to configure Yabeda Puma:

https://github.com/yabeda-rb/yabeda-puma-plugin#usage

What did you expect to happen?

Rails starting.

What actually happened?

Rails hangs when running initializers:

https://github.com/rails/rails/blob/main/railties/lib/rails/initializable.rb#L61

Additional context

Downgrading anyway to 2.1.0 fixes the problem because the problem is caused by this code:

https://github.com/palkan/anyway_config/blob/master/lib/anyway/ext/hash.rb#L30

Also, commenting the #deep_merge! also fixes the initialization.

Environment

Ruby Version:

3.0.1

Framework Version (Rails, whatever):

Rails 6.1.4

Anyway Config Version:

Fails with the latest version, is ok with 2.1.0

Is it possible to share a single app.yml with nested config for different classes?

Hello. I am in the process of converting my project to use anyway config and really liking the gem so far. I have one question which may be possible with the gem as-is, but can't seem to get it to work.

Essentially, I'd like to avoid having one yml file per class so all actual settings are in one place. So given the following,

class AppConfig < Anyway::Config
end

class RedisConfig < AppConfig
   attr_config :url
   required :url
end

class  DomainConfig < AppConfig
   attr_config :admin_portal
   required :admin_portal
end

I'd like to have a single app.yml that looks like this

development:
  domain:
    admin_portal:  development.example.com
  redis:
    url: localhost:6379

test:
  domain:
    admin_portal:  test.example.com
  redis:
    url: localhost:6379

Rather than individual domain.yml and redis.yml files. I've been playing around with config_name and some other paths but stumped so figured I'd ask before going too far down a rabbit hole or giving up.

Thanks!

Rails custom inflection support

What did you do?

I've a config file in Rails application that follow an inflection acronym.

# config/initializers/inflections.rb

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym "API"
end
# config/configs/payzen_api_config.rb

class PayzenAPIConfig < ApplicationConfig
  env_prefix :payzen_api
  attr_config :username, :password, :public_key, :sha256key
end

What did you expect to happen?

Code works and load correctly.

What actually happened?

I've got the following error.

expected file /home/user/Projects/rails_app/config/configs/payzen_api_config.rb to define constant PayzenApiConfig, but didn't

Additional context

I suspect eager load not to use rails loader or not the rails inflector.
https://github.com/palkan/anyway_config/blob/master/lib/anyway/railtie.rb#L16

Environment

Ruby Version: ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-linux]

Framework Version (Rails, whatever): Rails 6.0.3.7

Anyway Config Version: 2.1.0

Type cast precedence over describe_options

What did you do?

SOME_CONFIG_CLIENT_ID="123456.123456"

class SomeConfig < ApplicationConfig
  attr_config :client_id

  describe_options(
    # In this case, you should specify a hash with `type`
    # and (optionally) `desc` keys
    client_id: {
       desc: "client identificator",
       type: String
     }
  )
  
  required :client_id
end
$ SomeConfig.new.client_id
$ => 123456.12

# =>SomeConfig.new.client_id.class
# => Float

What did you expect to happen?

I was expecting the type to be string with full value

Environment

Ruby Version: 2.7

Framework Version (Rails, whatever): Rails 6.1.0.alpha

Anyway Config Version: 2.0.6

Am i doing something wrong or is it a bug?

Support for no env prefix

Is your feature request related to a problem? Please describe.

I am (considering) migrating a legacy codebase with >100 env vars and ~6 environments onto AnywayConfig.

Describe the solution you'd like

I would like to start our transition with one global AppConfig, and give teams the option to transition to namespaced configs and env vars as they see fit.

Therefore I would like to be able to pass in

class AppConfig < Anyway::Config
  env_prefix false

  attr_config(:foo)
end

and that result in AppConfig's foo attribute being set by the env var FOO.

Describe alternatives you've considered

I suppose I could write a custom "no namespace env" loader, but this would I believe affect all future Config objects since the loaders API is global only (I think?).

I could change my env vars, but this would be incredibly hazard prone to do all at once.

Requiring configurations on specific environments only.

Is your feature request related to a problem? Please describe.

This is probably a small and unpopular feature - mainly because there's an easy workaround. Still, it's a nice to have so suggesting it here. Basically we have some configurations that are only required in production (i.e. SomeConfig.new.private_key).

Describe the solution you'd like

I would suggest being able to set the required config to an environment. Given you have some.yml

default: &default
  host: "http://localhost"
  user: "user"

development:
  <<: *default

production:
  <<: *default

and in some_config.rb:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
    password: {only: :production}, # also consider {except: :development}
  )
end

Describe alternatives you've considered

We have some couple of alternatives:

Option A: Environment specific check

This is what we have, but it feels like it goes against the idea of AnywayConfig:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
  )
  if Rails.env.production?
    required(:password)
  end
end

Option B: Require it in all environments anyway

Not ideal, but also not a deal breaker:

class SomeConfig < ApplicationConfig
  attr_config(
    :host,
    :user,
    :password,
  )
  required(
    :host,
    :user,
    :password,
  )
end

WDYT?

Make Rails throw an error if a required field is not set when it starts

Is your feature request related to a problem? Please describe.

Anyway_config's required feature is great, but the function throws an error when an object is just initialized in Rails apps.
We don't realize to forget setting the required fields until initializing the object.

Describe the solution you'd like

I suggest the idea to make Rails fail if required items are not set in all Anyware_config inherited classes at the time of Rails after_initialize.

For the time being, to meet this requirement, I run the following script that initializes all classes inheriting from anyware_config.
The file is set under config/initializers.

files = Dir.glob("config/configs/*")

class_names = files
    .reject { |str| str.include?("application_config") }
    .map { |file_path| File.basename(file_path, ".rb").camelize } 
class_names.each { |name| name.constantize.new }

Random segfaults with Rails on Ruby 2.7

See https://bugs.ruby-lang.org/issues/17382

Although that's definitely an MRI issue, we can fix it at our end.
The feature is used for pretty printing:

using(Module.new do
refine Hash do
def inspect
"{#{map { |k, v| "#{k}: #{v.inspect}" }.join(", ")}}"
end
end

We can either make it explicit or, maybe, using a unique name would work:

using(Module.new do
  refine Object do
     alias __pp__ inspect
  end
  
  refine Hash do
     def __pp__
       # ...
     end
  end
end)

And then call v.__pp__ for all the printed values.

Add OptionParse integration

Add an ability to use config as option parser (e.g. for CLI apps/libraries).

Example usage:

class MyConfig < Anyway::Config
  attr_config :host, :port, :log_level
end

config = MyConfig.new

# For CLI that would be: config.parse_options!(ARGV)
config.parse_options!(%w(--host localhost --port 3333 --log-level debug))

config.host #=> "localhost"
config.port #=> 3333 (number)
config.log_level #=> "debug"

Under the hood an OptionParser instance is created with all the configuration parameters:

OptionParser.new do |o|
  # one-letter shortcut automatically added if:
  # - param name is a one word (i.e. "host" and not "http_host")
  # - the shortcut hasn't been defined already (i.e. the order config params defined matters)
  o.on "-h", "--host" do |arg|
    # we can re-use serialization from Anyway::Env class
    config.host = serialize_val(arg)
  end

  o.on "-p", "--port" do |arg|
    config.port = serialize_val(arg)
  end

  # snake_case params transformed into list-case (or kebab-case, choose yours)
  o.on "--log-level" do |arg|
    config.log_level = serialize_val(arg)
  end
end

There should be a way to customize the parser object (i.e. add banner or version/help handlers).
For that, parser generation should be extracted into a separate method (to make it easy to override/extend it):

class MyConfig < Anyway::Config
  # ...
  def build_option_parser
    super.tap do |parser|
      parser.banner = "mycli [options]"
     
      parser.on_tail "-h", "--help" do
        # ...
      end 
    end
  end
end

There should be a way to configure the configuration params for parser (e.g. you might want to exclude some parameters). For example:

class MyConfig < Anyway::Config
  attr_config :host, :log_level, :concurrency, server_args: {}

  # define which parameters could be accepted as options
  # (all by default) (i.e. we don't want to accept server_args)
  parser_options :host, :log_level, :concurrency

  # you can also provide a description for some params
  parse_options :host, :log_level, concurrency: "number of threads to use"
end

Unexpected behavior of #load_from_secrets when Rails app has not finished initialization

There could be a case when some config inherited from Anyway::Config (e.g., MyConfig < Anyway::Config) will be instantiated before Rails finished the initialization process. The only caveat here is that when I call MyConfig.new - inside there is a call #load_from_secrets which will ask Rails.application.secrets.
If that call happens before Rails finished initialization (e.g., during require of my gem) we could end up with broken Rails.application.secrets, here is why.

Inside Rails that method (#secrets) has memoization and read all the data only once, but secret_token and secret_key_base could be not yet loaded to config.

I suggest resetting the @secrets instance variable of Rails.application if Rails has not finished initialization yet. Otherwise, we'll have a pretty hard debugging case.

Type coercion for configs

Is your feature request related to a problem? Please describe.

It would be great to have type coercion so we don't have to do work-arounds for auto-casting. An example is when we had something like this:

# some_config.rb
class SomeConfig < ApplicationConfig
  attr_config :branch_code
end
# some_config.yml
some:
  branch_code: "123456"
$ SOME_BRANCH_CODE=123456 rails c
> SomeConfig.new.branch_code
> 123456

Describe the solution you'd like

I would suggest something like the describe_options but making it so it coerces the type when loading the data, instead of relying on a flag/workaround as suggested here: #68 (comment)

Suggested DSL:

class SomeConfig < ApplicationConfig
  attr_config :branch_code, type: String
end

which will enforce:

$ SOME_BRANCH_CODE=123456 rails c
> SomeConfig.new.branch_code
> "123456"

This probably means type needs to become a reserved word and you might need to group similar attrs like so:

class SomeConfig < ApplicationConfig
  attr_config :branch_code, :name, type: String
  attr_config :some_count, :age, type: Integer
end

Describe alternatives you've considered

The alternative is the suggested turning off of the auto-casting: skip_auto_serialization but not sure how far along that feature is.

edit
Another alternative (which I've implemented in our codebase) is to have a method to declare the types explicitly:

class SomeConfig < ApplicationConfig
  attr_config :branch_code, :name, :some_count, :age

  coerce_types(
    branch_code: :string,
    name: :string,
    some_count: :integer,
    age: :integer,
  )
end

P.S. I can volunteer to do this if this sounds like a good idea. Also welcome to shoot holes into this idea, potential bugs, gotchas etc. 🙏

delegate_missing_to :instance fails when config_attr is an existing class method.

What did you do?

The generator and guide suggest using delegate_missing_to :instance as a convenience for singleton behavior. This is incredibly handy but I just got bitten by it a 2nd time and I'm nervous about others incurring this problem down the line. Given this example, MyConfig.current_env and MyConfig.name will not resolve properly.

class ApplicationConfig < Anyway::Config
  class << self
    # Make it possible to access a singleton config instance
    delegate_missing_to :instance

    private

    def instance
      @instance ||= new
    end
end

class MyConfig < ApplicationConfig
    attr_config name: "my_config_name", 
                        current_env: "development",
                        etc.... # any instance method of ApplicationConfig
end

# Actual
MyConfig.name #=> ApplicationConfig
MyConfig.current_environment #=> Rails.env

# Expected
MyConfig.name #=> "my_config_name"
MyConfig.current_environment #=> "development"
(Or raise exception for shadowing if superclass has method defined) 

I understand the underlying issue, but it's a bit insidious in that it doesn't warn or fail eagerly when there is a collision and the attr is shadowed. I've renamed the attrs which solves the problem, but the delegate_missing_to in general seems risky. I'm not sure what the universal solution to this may be. I would love to see either:

  • attr_config method creation adds delegate <:attr> to :instance for each attr rather than (or in addition to) the universal missing delgate in the super class. This could also cause problems for existing methods used internally (e.g. current_env
  • attr_config raises an error if the method is defined on the super class to warn the user of the collision.

Environment

Ruby Version: 3.1.2

Framework Version (Rails, whatever): Rails 7.0.4

Anyway Config Version: 2.3.1

local files not loaded in rake task

By getting all details for this bug report I actually found the solution MyConfig.reload so maybe this is more of a documentation issue than functionality. But I was hoping that this would work out of the box in rake tasks.

What did you do?

I created a config class, i.e. StripeConfig and a local config file, i.e. config/stripe.local.yml containing product_id: prod_123.

Then I have a rake task to create a couple of available payment plans.

What did you expect to happen?

Rake task should be able to create plans for product with ID from the local config file.

What actually happened?

StripeConfig.product_id is nil and the rake task could not create plans for a product without an ID.

Additional context

I know that Anyway::Settings.use_local_files is only true in development/test environment. And it seems that neither RAILS_ENV nor RACK_ENV are set when I run my rake task.

Running the rake task with rails my_task or rake my_task didn't make any difference.

Running the rake task with RAILS_ENV=development rails my_task works.

Setting Anyway::Settings.use_local_files = true in my task, makes StripeConfig.new.product_id work but not StripeConfig.product_id. I don't mind setting use_local_files = true but maybe it's worth pointing out that users might want to StripeConfig.reload their config for this change to take affect. I can see this getting annoying with multiple configs too.

Environment

Ruby Version: ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]

Framework Version (Rails, whatever): Rails 6.0.2.2

Anyway Config Version: 2.0.1

Raising SyntaxError with Ruby 3.0.0

What did you do?

I followed the installation steps on a ruby project.

What did you expect to happen?

The gem loads with no issues.

What actually happened?

I am getting some SyntaxError exceptions.

/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway_config.rb:16:in `require': /Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:113: syntax error, unexpected instance variable (SyntaxError)
        end => @defaults
               ^~~~~~~~~
/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:123: syntax error, unexpected instance variable
...      end => @config_attributes
...             ^~~~~~~~~~~~~~~~~~
/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:141: syntax error, unexpected instance variable
...    end => @required_attributes
...           ^~~~~~~~~~~~~~~~~~~~
/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:161: syntax error, unexpected instance variable
        end => @load_callbacks
               ^~~~~~~~~~~~~~~
/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:192: syntax error, unexpected instance variable
        end => @env_prefix
               ^~~~~~~~~~~
/Users/fladson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/anyway_config-2.0.6/lib/anyway/config.rb:220: syntax error, unexpected instance variable
...       end => @accessors_module
...              ^~~~~~~~~~~~~~~~~

Additional context

# app/lib/foo_bar/config.rb
require "anyway_config"
module FooBar
  class Config < Anyway::Config
    attr_config :host
  end
end

# app/lib/foo_bar.rb
require_relative "foo_bar/config"
module FooBar
  def self.config
    @config ||= Config.new
  end
end

app/config/foo_bar.yml

test:
  host: localhost

development:
  host: localhost

staging:
  host: staging

production:
  host: production

Environment

macOS Big Sur

Ruby Version:
3.0.0

Framework Version (Rails, whatever):
N/A

Anyway Config Version:
2.0.6

Setting default_config_path in an initializer doesn't work.

What did you do?

I created an initializer

# config/initializers/anyway_config.rb
Rails.application.config.anyway_config do |config|
  config.default_config_path =  Rails.root.join("config", "settings")
end

What did you expect to happen?

I expected that my configuration files, in config/settings would be available to the application.

What actually happened?

Configuration values were empty.

Additional context

When I put a settings file into config/thing.yml the configured settings were available.

When I put the configuration line into config/application.rb the configured settings from config/settings were available.

# config/application.rb
config.anyway_config.default_config_path =  Rails.root.join("config", "settings")

Environment

Ruby Version: 2.7.5

Framework Version (Rails, whatever): 6.1.4.6

Anyway Config Version: 2.3.3

Upgrading from 2.1.0 to 2.2.0 causes rspec to hang

What did you do?

Upgraded anyway_config from 2.1.0 to 2.2.0

What did you expect to happen?

Everything continues to work

What actually happened?

rspec uses 100% cpu and hangs indefinitely. have to kill with SIG9 to stop it

Additional context

anyway_config is used by anycable_core and sniffer gems. All my gems are up to date except anyway_config.

Environment

Ruby Version:
3.0.1p64

Framework Version (Rails, whatever):
Rails 6.1.4.1

Anyway Config Version:
2.1.0 works, 2.2.0 and 2.2.1 have the issue.

to_hash method would be useful

A to_hash or to_h method callable on MyCoolGem.config would be useful to pass the values around in bulk, without having to call the specific attribute name.
MyCoolGem.config.load kinda return an hash but it calls load_from_sources which may not be the behavior expected.

SyntaxError raised on Anyway::Tracing when in Ruby 2.5

Hey there, thanks so much this gem firstly, this was a life-saver tbh 👍

What did you do?

the ... syntax in https://github.com/palkan/anyway_config/blob/master/lib/anyway/tracing.rb#L76-L79 and https://github.com/palkan/anyway_config/blob/master/lib/anyway/tracing.rb#L31-L33 sadly causes SyntaxError on my side on a Ruby 2.5.8 runtime.

IIUC, this argument forwarding syntax seems to be a feature available only from Ruby 2.7 onwards?

If so, backwards compatibility for < 2.7 may be broken?

`require': /[redacted]/vendor/bundle/ruby/2.5.0/gems/anyway_config-2.0.0/lib/anyway/tracing.rb:33: syntax error, unexpected ..., expecting ')' (SyntaxError)
      def dig(...)
              ^~~
/[redacted]/vendor/bundle/ruby/2.5.0/gems/anyway_config-2.0.0/lib/anyway/tracing.rb:34: syntax error, unexpected ..., expecting ')'
        value.dig(...)
                  ^~~
/[redacted]/vendor/bundle/ruby/2.5.0/gems/anyway_config-2.0.0/lib/anyway/tracing.rb:77: syntax error, unexpected ..., expecting ')'
      def keep_if(...)
                  ^~~
/[redacted]/vendor/bundle/ruby/2.5.0/gems/anyway_config-2.0.0/lib/anyway/tracing.rb:79: syntax error, unexpected ..., expecting ')'
        value.keep_if(...)
                      ^~~

Additional context

Environment

Ruby Version:

2.5.8

Framework Version (Rails, whatever):

nil

Anyway Config Version:

2.0.0

Tag Master Branch

The release 2.0.6 doesn't allow to set the known_environments like Rails.application.config.anyway_config.known_environments << "staging". However the Master branch does. Could you please tag Master so that we could use the new feature?
Thanks!

Multiple sources of config files

Is your feature request related to a problem? Please describe.

We're using Fastlane, but my background is NodeJs and Python. I'm a complete Ruby noob.

In the nodejs world we have the awesome nconf tool to allow us to do something very similar to what anway_config does, except with one important difference, I can easily control the load order and I can easily specify as many file sources as I like.

An example of how I would solve this in Nodejs is

this'd be in a npm module whose module name was '@us/config'

# values found in sources win based on when the source is defined. earlier sources win over later sources.

const nconf = require("nconf");
const REPO_ROOT = "a path to the root of the repo"

module.exports = function options({
  extra_file_sources = []
}) {

# argv source
nconf.argv()

# envvar source
nconf.env({
  separator: "__",
  whitelist: /^MY_ENVVAR_PREFIX_/
})

# file source
nconf.file(`${REPO_ROOT}/config.json`);

extra_file_sources.forEach(source => nconf.file(`${source}/config.json`); )

# returns the resulting compiled tree of data.
return nconf.get()
}

now i can use the above ./packages/config/index.js like so :

# in ./packages/web-server/index.js

function main () {
  # only loads the repo root config
  config = require('@us/config')()
}

or

# in ./packages/some_other_app/index.js

const HERE_DIR = __dirname;

function main () {
  # loads the repo root config, and what ever is in './settings'
  config = require('./services/config')([
    `${HERE_DIR}/settings`
  ])
}

Describe the solution you'd like

In my actualy use case we're using fastlane to build out our android and ios apps.

we have the bulk of the fastlane logic in a place like /packages/fastlane/Fastfile where this code below exists.

class OurConfig < Anyway::Config
  config_name :our_config
  env_prefix :our_config # now variables, starting wih `OUR_CONFIG_`, will be parsed

  Anyway::Settings.default_config_path = __dir__
  Anyway::Settings.extra_config_paths = [
    Dir.pwd()
  ]
end

then in our individual apps at places like /apps/hungarian_phrase_book/fastlane/Fastfile we import the above Fastfile and I'm expecting it to load both the /apps/hungarian_phrase_book/fastlane/config/whatever.yml and merge it all whilst also allowing for any value to be overridden from ENVVARS as usual.

Describe alternatives you've considered

the global gem , lets me source different files like I want, but doesn't allow for overriding with ENVVARS.
[dotenv] yeah nah.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.