anyway_config 1.4.4 → 2.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +187 -51
  3. data/lib/anyway.rb +22 -2
  4. data/lib/anyway/config.rb +116 -98
  5. data/lib/anyway/dynamic_config.rb +27 -0
  6. data/lib/anyway/env.rb +2 -5
  7. data/lib/anyway/ext/deep_dup.rb +4 -4
  8. data/lib/anyway/ext/deep_freeze.rb +5 -0
  9. data/lib/anyway/ext/jruby.rb +9 -4
  10. data/lib/anyway/ext/string_serialize.rb +1 -7
  11. data/{spec/dummy/config.ru → lib/anyway/loaders/env_loader.rb} +0 -0
  12. data/lib/anyway/loaders/secrets_loader.rb +0 -0
  13. data/lib/anyway/loaders/yaml_loader.rb +0 -0
  14. data/lib/anyway/option_parser_builder.rb +2 -2
  15. data/lib/anyway/optparse_config.rb +90 -0
  16. data/lib/anyway/rails/config.rb +76 -24
  17. data/lib/anyway/railtie.rb +11 -0
  18. data/lib/anyway/testing.rb +13 -0
  19. data/lib/anyway/testing/helpers.rb +36 -0
  20. data/lib/anyway/version.rb +1 -1
  21. metadata +46 -48
  22. data/.gem_release.yml +0 -3
  23. data/.gitignore +0 -40
  24. data/.rubocop.yml +0 -50
  25. data/.travis.yml +0 -30
  26. data/CHANGELOG.md +0 -112
  27. data/Gemfile +0 -21
  28. data/Rakefile +0 -23
  29. data/anyway_config.gemspec +0 -29
  30. data/config/cool.yml +0 -5
  31. data/gemfiles/jruby.gemfile +0 -7
  32. data/gemfiles/rails42.gemfile +0 -6
  33. data/gemfiles/rails5.gemfile +0 -6
  34. data/gemfiles/railsmaster.gemfile +0 -7
  35. data/spec/anyway.yml +0 -2
  36. data/spec/config_spec.rb +0 -337
  37. data/spec/config_spec_norails.rb +0 -86
  38. data/spec/dummy/config/application.rb +0 -13
  39. data/spec/dummy/config/boot.rb +0 -5
  40. data/spec/dummy/config/cool.yml +0 -5
  41. data/spec/dummy/config/cool_custom.yml +0 -2
  42. data/spec/dummy/config/database.yml +0 -25
  43. data/spec/dummy/config/environment.rb +0 -5
  44. data/spec/dummy/config/environments/test.rb +0 -2
  45. data/spec/dummy/config/routes.rb +0 -2
  46. data/spec/dummy/config/secrets.yml +0 -30
  47. data/spec/env_spec.rb +0 -50
  48. data/spec/ext/deep_dup_spec.rb +0 -38
  49. data/spec/ext/deep_freeze_spec.rb +0 -32
  50. data/spec/ext/hash_spec.rb +0 -39
  51. data/spec/ext/string_serialize_spec.rb +0 -32
  52. data/spec/spec_helper.rb +0 -31
  53. data/spec/spec_norails_helper.rb +0 -26
  54. data/spec/support/cool_config.rb +0 -10
  55. data/spec/support/print_warning_matcher.rb +0 -34
  56. data/spec/support/small_config.rb +0 -7
  57. data/spec/support/test_config.rb +0 -16
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ # Adds ability to generate anonymous (class-less) config dynamicly
5
+ # (like Rails.application.config_for but using more data sources).
6
+ module DynamicConfig
7
+ module ClassMethods
8
+ # Load config as Hash by any name
9
+ #
10
+ # Example:
11
+ #
12
+ # my_config = Anyway::Config.for(:my_app)
13
+ # # will load data from config/my_app.yml, secrets.my_app, ENV["MY_APP_*"]
14
+ #
15
+ def for(name, **options)
16
+ config = allocate
17
+ options[:env_prefix] ||= name.to_s.upcase
18
+ options[:config_path] ||= config.resolve_config_path(name, options[:env_prefix])
19
+ config.load_from_sources(name: name, **options)
20
+ end
21
+ end
22
+
23
+ def self.included(base)
24
+ base.extend ClassMethods
25
+ end
26
+ end
27
+ end
data/lib/anyway/env.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'anyway/ext/deep_dup'
4
- require 'anyway/ext/string_serialize'
5
-
6
3
  module Anyway
7
4
  # Parses environment variables and provides
8
5
  # method-like access
@@ -19,7 +16,7 @@ module Anyway
19
16
  end
20
17
 
21
18
  def fetch(prefix)
22
- @data[prefix] ||= parse_env(prefix.to_s.upcase)
19
+ @data[prefix] ||= parse_env(prefix)
23
20
  @data[prefix].deep_dup
24
21
  end
25
22
 
@@ -29,7 +26,7 @@ module Anyway
29
26
  ENV.each_pair.with_object({}) do |(key, val), data|
30
27
  next unless key.start_with?(prefix)
31
28
 
32
- path = key.sub(/^#{prefix}_/, '').downcase
29
+ path = key.sub(/^#{prefix}_/, "").downcase
33
30
  set_by_path(data, path, val.serialize)
34
31
  end
35
32
  end
@@ -9,10 +9,10 @@ module Anyway
9
9
  def deep_dup
10
10
  each_with_object(dup) do |(key, value), hash|
11
11
  hash[key] = if value.is_a?(::Hash) || value.is_a?(::Array)
12
- value.deep_dup
13
- else
14
- value
15
- end
12
+ value.deep_dup
13
+ else
14
+ value
15
+ end
16
16
  end
17
17
  end
18
18
  end
@@ -22,6 +22,11 @@ module Anyway
22
22
  end
23
23
  end
24
24
 
25
+ begin
26
+ require "active_support/core_ext/hash/indifferent_access"
27
+ rescue LoadError
28
+ end
29
+
25
30
  if defined?(::ActiveSupport::HashWithIndifferentAccess)
26
31
  refine ::ActiveSupport::HashWithIndifferentAccess do
27
32
  def deep_freeze
@@ -13,10 +13,10 @@ module Anyway
13
13
  def deep_dup
14
14
  each_with_object(dup) do |(key, value), hash|
15
15
  hash[key] = if value.is_a?(::Hash) || value.is_a?(::Array)
16
- value.deep_dup
17
- else
18
- value
19
- end
16
+ value.deep_dup
17
+ else
18
+ value
19
+ end
20
20
  end
21
21
  end
22
22
 
@@ -74,6 +74,11 @@ module Anyway
74
74
  end
75
75
  end
76
76
 
77
+ begin
78
+ require "active_support/core_ext/hash/indifferent_access"
79
+ rescue LoadError
80
+ end
81
+
77
82
  if defined?(::ActiveSupport::HashWithIndifferentAccess)
78
83
  refine ::ActiveSupport::HashWithIndifferentAccess do
79
84
  def deep_freeze
@@ -10,14 +10,10 @@ module Anyway
10
10
  ARRAY_RXP = /\A[^'"].*\s*,\s*.*[^'"]\z/
11
11
 
12
12
  refine ::String do
13
- # rubocop:disable Metrics/MethodLength
14
- # rubocop:disable Metrics/CyclomaticComplexity
15
13
  def serialize
16
14
  case self
17
15
  when ARRAY_RXP
18
- # rubocop:disable Style/SymbolProc
19
16
  split(/\s*,\s*/).map { |word| word.serialize }
20
- # rubocop:enable Style/SymbolProc
21
17
  when /\A(true|t|yes|y)\z/i
22
18
  true
23
19
  when /\A(false|f|no|n)\z/i
@@ -29,13 +25,11 @@ module Anyway
29
25
  when /\A\d*\.\d+\z/
30
26
  to_f
31
27
  when /\A['"].*['"]\z/
32
- gsub(/(\A['"]|['"]\z)/, '')
28
+ gsub(/(\A['"]|['"]\z)/, "")
33
29
  else
34
30
  self
35
31
  end
36
32
  end
37
- # rubocop:enable Metrics/MethodLength
38
- # rubocop:enable Metrics/CyclomaticComplexity
39
33
  end
40
34
 
41
35
  using self
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'optparse'
3
+ require "optparse"
4
4
 
5
5
  module Anyway # :nodoc:
6
6
  # Initializes the OptionParser instance using the given configuration
@@ -19,7 +19,7 @@ module Anyway # :nodoc:
19
19
  private
20
20
 
21
21
  def option_parser_on_args(key, flag: false, desc: nil)
22
- on_args = ["--#{key.to_s.tr('_', '-')}#{flag ? '' : ' VALUE'}"]
22
+ on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
23
23
  on_args << desc unless desc.nil?
24
24
  on_args
25
25
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway/option_parser_builder"
4
+
5
+ require "anyway/ext/deep_dup"
6
+ require "anyway/ext/string_serialize"
7
+
8
+ module Anyway
9
+ using Anyway::Ext::DeepDup
10
+ using Anyway::Ext::StringSerialize
11
+
12
+ # Adds ability to use script options as the source
13
+ # of configuration (via optparse)
14
+ module OptparseConfig
15
+ module ClassMethods
16
+ def ignore_options(*args)
17
+ args.each do |name|
18
+ option_parser_descriptors[name.to_s][:ignore] = true
19
+ end
20
+ end
21
+
22
+ def describe_options(**hargs)
23
+ hargs.each do |name, desc|
24
+ option_parser_descriptors[name.to_s][:desc] = desc
25
+ end
26
+ end
27
+
28
+ def flag_options(*args)
29
+ args.each do |name|
30
+ option_parser_descriptors[name.to_s][:flag] = true
31
+ end
32
+ end
33
+
34
+ def extend_options(&block)
35
+ option_parser_extensions << block
36
+ end
37
+
38
+ def option_parser_options
39
+ config_attributes.each_with_object({}) do |key, result|
40
+ descriptor = option_parser_descriptors[key.to_s]
41
+ next if descriptor[:ignore] == true
42
+
43
+ result[key] = descriptor
44
+ end
45
+ end
46
+
47
+ def option_parser_extensions
48
+ return @option_parser_extensions if instance_variable_defined?(:@option_parser_extensions)
49
+
50
+ @option_parser_extensions =
51
+ if superclass < Anyway::Config
52
+ superclass.option_parser_extensions.dup
53
+ else
54
+ []
55
+ end
56
+ end
57
+
58
+ def option_parser_descriptors
59
+ return @option_parser_descriptors if instance_variable_defined?(:@option_parser_descriptors)
60
+
61
+ @option_parser_descriptors =
62
+ if superclass < Anyway::Config
63
+ superclass.option_parser_descriptors.deep_dup
64
+ else
65
+ Hash.new { |h, k| h[k] = {} }
66
+ end
67
+ end
68
+ end
69
+
70
+ def option_parser
71
+ @option_parser ||= begin
72
+ OptionParserBuilder.call(self.class.option_parser_options) do |key, arg|
73
+ set_value(key, arg.is_a?(String) ? arg.serialize : arg)
74
+ end.tap do |parser|
75
+ self.class.option_parser_extensions.map do |extension|
76
+ extension.call(parser, self)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def parse_options!(options)
83
+ option_parser.parse!(options)
84
+ end
85
+
86
+ def self.included(base)
87
+ base.extend ClassMethods
88
+ end
89
+ end
90
+ end
@@ -1,38 +1,90 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+
3
5
  module Anyway
4
- class Config # :nodoc:
5
- class << self
6
- def defaults
7
- return unless @defaults
6
+ module Rails
7
+ # Enhance config to be more Railsy-like:
8
+ # – accept hashes with indeferent access
9
+ # - load data from secrets
10
+ # - recognize Rails env when loading from YML
11
+ module Config
12
+ module ClassMethods
13
+ # Make defaults to be a Hash with indifferent access
14
+ def defaults
15
+ return @defaults if instance_variable_defined?(:@defaults)
8
16
 
9
- @defaults_wia ||= @defaults.with_indifferent_access
17
+ @defaults = super.with_indifferent_access
18
+ end
10
19
  end
11
- end
12
20
 
13
- def load_from_sources(config = {})
14
- config = config.with_indifferent_access
15
- load_from_file(config)
16
- load_from_secrets(config)
17
- load_from_env(config)
18
- end
21
+ def load_from_sources(**options)
22
+ base_config = {}.with_indifferent_access
23
+ each_source(options) do |config|
24
+ base_config.deep_merge!(config) if config
25
+ end
26
+ base_config
27
+ end
19
28
 
20
- def load_from_file(config)
21
- config.deep_merge!(parse_yml(config_path)[Rails.env] || {}) if File.file? config_path
22
- config
23
- end
29
+ def each_source(options)
30
+ yield load_from_file(options)
31
+ yield load_from_secrets(options)
32
+ yield load_from_credentials(options)
33
+ yield load_from_env(options)
34
+ end
24
35
 
25
- def load_from_secrets(config)
26
- if Rails.application.respond_to?(:secrets)
27
- config.deep_merge!(Rails.application.secrets.send(@config_name) || {})
36
+ def load_from_file(name:, config_path:, env_prefix:, **_options)
37
+ file_config = load_from_yml(config_path)[::Rails.env] || {}
38
+
39
+ if Anyway::Settings.use_local_files
40
+ local_config_path = config_path.sub(/\.yml/, ".local.yml")
41
+ file_config.deep_merge!(load_from_yml(local_config_path) || {})
42
+ end
43
+
44
+ file_config
45
+ end
46
+
47
+ def load_from_secrets(name:, **_options)
48
+ return unless ::Rails.application.respond_to?(:secrets)
49
+
50
+ ::Rails.application.secrets.public_send(name)
28
51
  end
29
- config
30
- end
31
52
 
32
- private
53
+ def load_from_credentials(name:, **_options)
54
+ # do not load from credentials if we're in the context
55
+ # of the `credentials:edit` command
56
+ return if defined?(::Rails::Command::CredentialsCommand)
33
57
 
34
- def default_config_path
35
- Rails.root.join("config", "#{config_name}.yml")
58
+ return unless ::Rails.application.respond_to?(:credentials)
59
+
60
+ # Create a new hash cause credentials are mutable!
61
+ creds_config = {}
62
+
63
+ creds_config.deep_merge!(::Rails.application.credentials.public_send(name) || {})
64
+
65
+ creds_config.deep_merge!(load_from_local_credentials(name: name)) if Anyway::Settings.use_local_files
66
+ creds_config
67
+ end
68
+
69
+ def load_from_local_credentials(name:)
70
+ local_creds_path = ::Rails.root.join("config/credentials/local.yml.enc").to_s
71
+
72
+ return unless File.file?(local_creds_path)
73
+
74
+ creds = ::Rails.application.encrypted(
75
+ local_creds_path,
76
+ key_path: ::Rails.root.join("config/credentials/local.key")
77
+ )
78
+
79
+ creds.public_send(name) || {}
80
+ end
81
+
82
+ def default_config_path(name)
83
+ ::Rails.root.join("config", "#{name}.yml")
84
+ end
36
85
  end
37
86
  end
38
87
  end
88
+
89
+ Anyway::Config.prepend Anyway::Rails::Config
90
+ Anyway::Config.singleton_class.prepend Anyway::Rails::Config::ClassMethods
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway # :nodoc:
4
+ class Railtie < ::Rails::Railtie # :nodoc:
5
+ # Add settings to Rails config
6
+ config.anyway_config = Anyway::Settings
7
+
8
+ # Allow autoloading of app/configs in configuration files
9
+ ActiveSupport::Dependencies.autoload_paths << "app/configs"
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway/testing/helpers"
4
+
5
+ if defined?(RSpec)
6
+ RSpec.configure do |config|
7
+ config.include(
8
+ Anyway::Testing::Helpers,
9
+ type: :config,
10
+ file_path: %r{spec/configs}
11
+ )
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Testing
5
+ module Helpers
6
+ # Sets the ENV variables to the provided
7
+ # values and restore outside the block
8
+ #
9
+ # Also resets Anyway.env before and after calling the block
10
+ # to make sure that the values are not cached.
11
+ #
12
+ # NOTE: to remove the env value, pass `nil` as the value
13
+ def with_env(data)
14
+ was_values = []
15
+
16
+ data.each do |key, val|
17
+ was_values << [key, ENV[key]]
18
+ next ENV.delete(key) if val.nil?
19
+ ENV[key] = val
20
+ end
21
+
22
+ # clear cached env values
23
+ Anyway.env.clear
24
+ yield
25
+ ensure
26
+ was_values.each do |(key, val)|
27
+ next ENV.delete(key) if val.nil?
28
+ ENV[key] = val
29
+ end
30
+
31
+ # clear cache again
32
+ Anyway.env.clear
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway # :nodoc:
4
- VERSION = "1.4.4"
4
+ VERSION = "2.0.0.pre"
5
5
  end