anyway_config 1.4.4 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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