anyway_config 2.0.6 → 2.2.2
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.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +241 -181
 - data/README.md +237 -12
 - data/lib/.rbnext/1995.next/anyway/config.rb +438 -0
 - data/lib/.rbnext/1995.next/anyway/dynamic_config.rb +31 -0
 - data/lib/.rbnext/1995.next/anyway/env.rb +56 -0
 - data/lib/.rbnext/1995.next/anyway/loaders/base.rb +21 -0
 - data/lib/.rbnext/1995.next/anyway/tracing.rb +181 -0
 - data/lib/.rbnext/2.7/anyway/auto_cast.rb +39 -19
 - data/lib/.rbnext/2.7/anyway/config.rb +60 -15
 - data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +30 -0
 - data/lib/.rbnext/2.7/anyway/rbs.rb +92 -0
 - data/lib/.rbnext/2.7/anyway/settings.rb +79 -0
 - data/lib/.rbnext/2.7/anyway/tracing.rb +1 -1
 - data/lib/.rbnext/2.7/anyway/type_casting.rb +143 -0
 - data/lib/.rbnext/3.0/anyway/auto_cast.rb +53 -0
 - data/lib/.rbnext/{2.8 → 3.0}/anyway/config.rb +60 -15
 - data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders/base.rb +0 -0
 - data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders.rb +0 -0
 - data/lib/.rbnext/{2.8 → 3.0}/anyway/tracing.rb +1 -1
 - data/lib/anyway/auto_cast.rb +39 -19
 - data/lib/anyway/config.rb +74 -29
 - data/lib/anyway/dynamic_config.rb +6 -2
 - data/lib/anyway/env.rb +1 -1
 - data/lib/anyway/ext/deep_dup.rb +12 -0
 - data/lib/anyway/ext/hash.rb +0 -12
 - data/lib/anyway/loaders/base.rb +1 -1
 - data/lib/anyway/loaders/env.rb +3 -1
 - data/lib/anyway/loaders/yaml.rb +9 -5
 - data/lib/anyway/option_parser_builder.rb +1 -3
 - data/lib/anyway/optparse_config.rb +5 -7
 - data/lib/anyway/rails/loaders/credentials.rb +4 -4
 - data/lib/anyway/rails/loaders/secrets.rb +6 -8
 - data/lib/anyway/rails/loaders/yaml.rb +11 -0
 - data/lib/anyway/rails/settings.rb +9 -2
 - data/lib/anyway/rbs.rb +92 -0
 - data/lib/anyway/settings.rb +52 -2
 - data/lib/anyway/tracing.rb +6 -6
 - data/lib/anyway/type_casting.rb +134 -0
 - data/lib/anyway/utils/deep_merge.rb +21 -0
 - data/lib/anyway/version.rb +1 -1
 - data/lib/anyway_config.rb +4 -0
 - data/sig/anyway_config.rbs +129 -0
 - metadata +42 -15
 - data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +0 -31
 
    
        data/lib/anyway/env.rb
    CHANGED
    
    | 
         @@ -49,7 +49,7 @@ module Anyway 
     | 
|
| 
       49 
49 
     | 
    
         
             
                    path = key.sub(/^#{prefix}_/, "").downcase
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
51 
     | 
    
         
             
                    paths = path.split("__")
         
     | 
| 
       52 
     | 
    
         
            -
                    trace!(:env, *paths, key: 
     | 
| 
      
 52 
     | 
    
         
            +
                    trace!(:env, *paths, key:) { data.bury(type_cast.call(val), *paths) }
         
     | 
| 
       53 
53 
     | 
    
         
             
                  end
         
     | 
| 
       54 
54 
     | 
    
         
             
                end
         
     | 
| 
       55 
55 
     | 
    
         
             
              end
         
     | 
    
        data/lib/anyway/ext/deep_dup.rb
    CHANGED
    
    
    
        data/lib/anyway/ext/hash.rb
    CHANGED
    
    | 
         @@ -5,18 +5,6 @@ module Anyway 
     | 
|
| 
       5 
5 
     | 
    
         
             
                # Extend Hash through refinements
         
     | 
| 
       6 
6 
     | 
    
         
             
                module Hash
         
     | 
| 
       7 
7 
     | 
    
         
             
                  refine ::Hash do
         
     | 
| 
       8 
     | 
    
         
            -
                    # From ActiveSupport http://api.rubyonrails.org/classes/Hash.html#method-i-deep_merge
         
     | 
| 
       9 
     | 
    
         
            -
                    def deep_merge!(other_hash)
         
     | 
| 
       10 
     | 
    
         
            -
                      merge!(other_hash) do |key, this_value, other_value|
         
     | 
| 
       11 
     | 
    
         
            -
                        if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
         
     | 
| 
       12 
     | 
    
         
            -
                          this_value.deep_merge!(other_value)
         
     | 
| 
       13 
     | 
    
         
            -
                          this_value
         
     | 
| 
       14 
     | 
    
         
            -
                        else
         
     | 
| 
       15 
     | 
    
         
            -
                          other_value
         
     | 
| 
       16 
     | 
    
         
            -
                        end
         
     | 
| 
       17 
     | 
    
         
            -
                      end
         
     | 
| 
       18 
     | 
    
         
            -
                    end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
8 
     | 
    
         
             
                    def stringify_keys!
         
     | 
| 
       21 
9 
     | 
    
         
             
                      keys.each do |key|
         
     | 
| 
       22 
10 
     | 
    
         
             
                        value = delete(key)
         
     | 
    
        data/lib/anyway/loaders/base.rb
    CHANGED
    
    
    
        data/lib/anyway/loaders/env.rb
    CHANGED
    
    | 
         @@ -6,7 +6,9 @@ module Anyway 
     | 
|
| 
       6 
6 
     | 
    
         
             
              module Loaders
         
     | 
| 
       7 
7 
     | 
    
         
             
                class Env < Base
         
     | 
| 
       8 
8 
     | 
    
         
             
                  def call(env_prefix:, **_options)
         
     | 
| 
       9 
     | 
    
         
            -
                     
     | 
| 
      
 9 
     | 
    
         
            +
                    env = ::Anyway::Env.new(type_cast: ::Anyway::NoCast)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    env.fetch_with_trace(env_prefix).then do |(conf, trace)|
         
     | 
| 
       10 
12 
     | 
    
         
             
                      Tracing.current_trace&.merge!(trace)
         
     | 
| 
       11 
13 
     | 
    
         
             
                      conf
         
     | 
| 
       12 
14 
     | 
    
         
             
                    end
         
     | 
    
        data/lib/anyway/loaders/yaml.rb
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ module Anyway 
     | 
|
| 
       17 
17 
     | 
    
         
             
                    local_path = local_config_path(config_path)
         
     | 
| 
       18 
18 
     | 
    
         
             
                    local_config = trace!(:yml, path: relative_config_path(local_path).to_s) { load_local_yml(local_path) }
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
      
 20 
     | 
    
         
            +
                    Utils.deep_merge!(base_config, local_config)
         
     | 
| 
       21 
21 
     | 
    
         
             
                  end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -25,15 +25,19 @@ module Anyway 
     | 
|
| 
       25 
25 
     | 
    
         
             
                  def parse_yml(path)
         
     | 
| 
       26 
26 
     | 
    
         
             
                    return {} unless File.file?(path)
         
     | 
| 
       27 
27 
     | 
    
         
             
                    require "yaml" unless defined?(::YAML)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    # By default, YAML load will return `false` when the yaml document is
         
     | 
| 
      
 30 
     | 
    
         
            +
                    # empty. When this occurs, we return an empty hash instead, to match
         
     | 
| 
      
 31 
     | 
    
         
            +
                    # the interface when no config file is present.
         
     | 
| 
       28 
32 
     | 
    
         
             
                    if defined?(ERB)
         
     | 
| 
       29 
     | 
    
         
            -
                      ::YAML.load(ERB.new(File.read(path)).result) # rubocop:disable Security/YAMLLoad
         
     | 
| 
      
 33 
     | 
    
         
            +
                      ::YAML.load(ERB.new(File.read(path)).result) || {} # rubocop:disable Security/YAMLLoad
         
     | 
| 
       30 
34 
     | 
    
         
             
                    else
         
     | 
| 
       31 
     | 
    
         
            -
                      ::YAML.load_file(path)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      ::YAML.load_file(path) || {}
         
     | 
| 
       32 
36 
     | 
    
         
             
                    end
         
     | 
| 
       33 
37 
     | 
    
         
             
                  end
         
     | 
| 
       34 
38 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                   
     | 
| 
       36 
     | 
    
         
            -
                   
     | 
| 
      
 39 
     | 
    
         
            +
                  alias_method :load_base_yml, :parse_yml
         
     | 
| 
      
 40 
     | 
    
         
            +
                  alias_method :load_local_yml, :parse_yml
         
     | 
| 
       37 
41 
     | 
    
         | 
| 
       38 
42 
     | 
    
         
             
                  def local_config_path(path)
         
     | 
| 
       39 
43 
     | 
    
         
             
                    path.sub(/\.yml/, ".local.yml")
         
     | 
| 
         @@ -8,8 +8,6 @@ module Anyway # :nodoc: 
     | 
|
| 
       8 
8 
     | 
    
         
             
                class << self
         
     | 
| 
       9 
9 
     | 
    
         
             
                  def call(options)
         
     | 
| 
       10 
10 
     | 
    
         
             
                    OptionParser.new do |opts|
         
     | 
| 
       11 
     | 
    
         
            -
                      opts.accept(AutoCast) { AutoCast.call(_1) }
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
11 
     | 
    
         
             
                      options.each do |key, descriptor|
         
     | 
| 
       14 
12 
     | 
    
         
             
                        opts.on(*option_parser_on_args(key, **descriptor)) do |val|
         
     | 
| 
       15 
13 
     | 
    
         
             
                          yield [key, val]
         
     | 
| 
         @@ -20,7 +18,7 @@ module Anyway # :nodoc: 
     | 
|
| 
       20 
18 
     | 
    
         | 
| 
       21 
19 
     | 
    
         
             
                  private
         
     | 
| 
       22 
20 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                  def option_parser_on_args(key, flag: false, desc: nil, type:  
     | 
| 
      
 21 
     | 
    
         
            +
                  def option_parser_on_args(key, flag: false, desc: nil, type: ::String)
         
     | 
| 
       24 
22 
     | 
    
         
             
                    on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
         
     | 
| 
       25 
23 
     | 
    
         
             
                    on_args << type unless flag
         
     | 
| 
       26 
24 
     | 
    
         
             
                    on_args << desc unless desc.nil?
         
     | 
| 
         @@ -70,13 +70,11 @@ module Anyway 
     | 
|
| 
       70 
70 
     | 
    
         
             
                end
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
       72 
72 
     | 
    
         
             
                def option_parser
         
     | 
| 
       73 
     | 
    
         
            -
                  @option_parser ||=  
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                     
     | 
| 
       77 
     | 
    
         
            -
                       
     | 
| 
       78 
     | 
    
         
            -
                        extension.call(parser, self)
         
     | 
| 
       79 
     | 
    
         
            -
                      end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @option_parser ||= OptionParserBuilder.call(self.class.option_parser_options) do |key, val|
         
     | 
| 
      
 74 
     | 
    
         
            +
                                       write_config_attr(key, val)
         
     | 
| 
      
 75 
     | 
    
         
            +
                                     end.tap do |parser|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    self.class.option_parser_extensions.map do |extension|
         
     | 
| 
      
 77 
     | 
    
         
            +
                      extension.call(parser, self)
         
     | 
| 
       80 
78 
     | 
    
         
             
                    end
         
     | 
| 
       81 
79 
     | 
    
         
             
                  end
         
     | 
| 
       82 
80 
     | 
    
         
             
                end
         
     | 
| 
         @@ -22,15 +22,15 @@ module Anyway 
     | 
|
| 
       22 
22 
     | 
    
         
             
                        :credentials,
         
     | 
| 
       23 
23 
     | 
    
         
             
                        store: credentials_path
         
     | 
| 
       24 
24 
     | 
    
         
             
                      ) do
         
     | 
| 
       25 
     | 
    
         
            -
                        ::Rails.application.credentials. 
     | 
| 
      
 25 
     | 
    
         
            +
                        ::Rails.application.credentials.config[name.to_sym]
         
     | 
| 
       26 
26 
     | 
    
         
             
                      end.then do |creds|
         
     | 
| 
       27 
     | 
    
         
            -
                         
     | 
| 
      
 27 
     | 
    
         
            +
                        Utils.deep_merge!(config, creds) if creds
         
     | 
| 
       28 
28 
     | 
    
         
             
                      end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                      if use_local?
         
     | 
| 
       31 
31 
     | 
    
         
             
                        trace!(:credentials, store: LOCAL_CONTENT_PATH) do
         
     | 
| 
       32 
32 
     | 
    
         
             
                          local_credentials(name)
         
     | 
| 
       33 
     | 
    
         
            -
                        end.then { |creds|  
     | 
| 
      
 33 
     | 
    
         
            +
                        end.then { |creds| Utils.deep_merge!(config, creds) if creds }
         
     | 
| 
       34 
34 
     | 
    
         
             
                      end
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
                      config
         
     | 
| 
         @@ -48,7 +48,7 @@ module Anyway 
     | 
|
| 
       48 
48 
     | 
    
         
             
                        key_path: ::Rails.root.join("config/credentials/local.key")
         
     | 
| 
       49 
49 
     | 
    
         
             
                      )
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                      creds. 
     | 
| 
      
 51 
     | 
    
         
            +
                      creds.config[name.to_sym]
         
     | 
| 
       52 
52 
     | 
    
         
             
                    end
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
54 
     | 
    
         
             
                    def credentials_path
         
     | 
| 
         @@ -15,7 +15,7 @@ module Anyway 
     | 
|
| 
       15 
15 
     | 
    
         
             
                      trace!(:secrets) do
         
     | 
| 
       16 
16 
     | 
    
         
             
                        secrets.public_send(name)
         
     | 
| 
       17 
17 
     | 
    
         
             
                      end.then do |secrets|
         
     | 
| 
       18 
     | 
    
         
            -
                         
     | 
| 
      
 18 
     | 
    
         
            +
                        Utils.deep_merge!(config, secrets) if secrets
         
     | 
| 
       19 
19 
     | 
    
         
             
                      end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                      config
         
     | 
| 
         @@ -24,13 +24,11 @@ module Anyway 
     | 
|
| 
       24 
24 
     | 
    
         
             
                    private
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                    def secrets
         
     | 
| 
       27 
     | 
    
         
            -
                      @secrets ||=  
     | 
| 
       28 
     | 
    
         
            -
                         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                          ::Rails.application.remove_instance_variable(:@secrets)
         
     | 
| 
       33 
     | 
    
         
            -
                        end
         
     | 
| 
      
 27 
     | 
    
         
            +
                      @secrets ||= ::Rails.application.secrets.tap do |_|
         
     | 
| 
      
 28 
     | 
    
         
            +
                        # Reset secrets state if the app hasn't been initialized
         
     | 
| 
      
 29 
     | 
    
         
            +
                        # See https://github.com/palkan/anyway_config/issues/14
         
     | 
| 
      
 30 
     | 
    
         
            +
                        next if ::Rails.application.initialized?
         
     | 
| 
      
 31 
     | 
    
         
            +
                        ::Rails.application.remove_instance_variable(:@secrets)
         
     | 
| 
       34 
32 
     | 
    
         
             
                      end
         
     | 
| 
       35 
33 
     | 
    
         
             
                    end
         
     | 
| 
       36 
34 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -5,11 +5,22 @@ module Anyway 
     | 
|
| 
       5 
5 
     | 
    
         
             
                module Loaders
         
     | 
| 
       6 
6 
     | 
    
         
             
                  class YAML < Anyway::Loaders::YAML
         
     | 
| 
       7 
7 
     | 
    
         
             
                    def load_base_yml(*)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      parsed_yml = super
         
     | 
| 
      
 9 
     | 
    
         
            +
                      return parsed_yml unless environmental?(parsed_yml)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       8 
11 
     | 
    
         
             
                      super[::Rails.env] || {}
         
     | 
| 
       9 
12 
     | 
    
         
             
                    end
         
     | 
| 
       10 
13 
     | 
    
         | 
| 
       11 
14 
     | 
    
         
             
                    private
         
     | 
| 
       12 
15 
     | 
    
         | 
| 
      
 16 
     | 
    
         
            +
                    def environmental?(parsed_yml)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      return true unless Settings.future.unwrap_known_environments
         
     | 
| 
      
 18 
     | 
    
         
            +
                      # likely
         
     | 
| 
      
 19 
     | 
    
         
            +
                      return true if parsed_yml.key?(::Rails.env)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      # less likely
         
     | 
| 
      
 21 
     | 
    
         
            +
                      ::Rails.application.config.anyway_config.known_environments.any? { parsed_yml.key?(_1) }
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       13 
24 
     | 
    
         
             
                    def relative_config_path(path)
         
     | 
| 
       14 
25 
     | 
    
         
             
                      Pathname.new(path).relative_path_from(::Rails.root)
         
     | 
| 
       15 
26 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -9,8 +9,13 @@ end 
     | 
|
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
            module Anyway
         
     | 
| 
       11 
11 
     | 
    
         
             
              class Settings
         
     | 
| 
      
 12 
     | 
    
         
            +
                class Future
         
     | 
| 
      
 13 
     | 
    
         
            +
                  setting :unwrap_known_environments, true
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       12 
16 
     | 
    
         
             
                class << self
         
     | 
| 
       13 
17 
     | 
    
         
             
                  attr_reader :autoload_static_config_path, :autoloader
         
     | 
| 
      
 18 
     | 
    
         
            +
                  attr_accessor :known_environments
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
20 
     | 
    
         
             
                  if defined?(::Zeitwerk)
         
     | 
| 
       16 
21 
     | 
    
         
             
                    def autoload_static_config_path=(val)
         
     | 
| 
         @@ -22,10 +27,11 @@ module Anyway 
     | 
|
| 
       22 
27 
     | 
    
         | 
| 
       23 
28 
     | 
    
         
             
                      @autoload_static_config_path = val
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                      # See https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
         
     | 
| 
      
 30 
     | 
    
         
            +
                      # See Rails 6 https://github.com/rails/rails/blob/8ab4fd12f18203b83d0f252db96d10731485ff6a/railties/lib/rails/autoloaders.rb#L10
         
     | 
| 
      
 31 
     | 
    
         
            +
                      # and Rails 7 https://github.com/rails/rails/blob/5462fbd5de1900c1b1ce1c9dc11c1a2d8cdcd809/railties/lib/rails/autoloaders.rb#L15
         
     | 
| 
       26 
32 
     | 
    
         
             
                      @autoloader = Zeitwerk::Loader.new.tap do |loader|
         
     | 
| 
       27 
33 
     | 
    
         
             
                        loader.tag = "anyway.config"
         
     | 
| 
       28 
     | 
    
         
            -
                        loader.inflector = ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector
         
     | 
| 
      
 34 
     | 
    
         
            +
                        loader.inflector = defined?(ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector) ? ActiveSupport::Dependencies::ZeitwerkIntegration::Inflector : ::Rails::Autoloaders::Inflector
         
     | 
| 
       29 
35 
     | 
    
         
             
                        loader.push_dir(::Rails.root.join(val))
         
     | 
| 
       30 
36 
     | 
    
         
             
                        loader.setup
         
     | 
| 
       31 
37 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -56,5 +62,6 @@ module Anyway 
     | 
|
| 
       56 
62 
     | 
    
         
             
                end
         
     | 
| 
       57 
63 
     | 
    
         | 
| 
       58 
64 
     | 
    
         
             
                self.default_config_path = ->(name) { ::Rails.root.join("config", "#{name}.yml") }
         
     | 
| 
      
 65 
     | 
    
         
            +
                self.known_environments = %w[test development production]
         
     | 
| 
       59 
66 
     | 
    
         
             
              end
         
     | 
| 
       60 
67 
     | 
    
         
             
            end
         
     | 
    
        data/lib/anyway/rbs.rb
    ADDED
    
    | 
         @@ -0,0 +1,92 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Anyway
         
     | 
| 
      
 4 
     | 
    
         
            +
              module RBSGenerator
         
     | 
| 
      
 5 
     | 
    
         
            +
                TYPE_TO_CLASS = {
         
     | 
| 
      
 6 
     | 
    
         
            +
                  string: "String",
         
     | 
| 
      
 7 
     | 
    
         
            +
                  integer: "Integer",
         
     | 
| 
      
 8 
     | 
    
         
            +
                  float: "Float",
         
     | 
| 
      
 9 
     | 
    
         
            +
                  date: "Date",
         
     | 
| 
      
 10 
     | 
    
         
            +
                  datetime: "DateTime",
         
     | 
| 
      
 11 
     | 
    
         
            +
                  uri: "URI",
         
     | 
| 
      
 12 
     | 
    
         
            +
                  boolean: "bool"
         
     | 
| 
      
 13 
     | 
    
         
            +
                }.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # Generate RBS signature from a config class
         
     | 
| 
      
 16 
     | 
    
         
            +
                def to_rbs
         
     | 
| 
      
 17 
     | 
    
         
            +
                  *namespace, class_name = name.split("::")
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  buf = []
         
     | 
| 
      
 20 
     | 
    
         
            +
                  indent = 0
         
     | 
| 
      
 21 
     | 
    
         
            +
                  interface_name = "_Config"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  if namespace.empty?
         
     | 
| 
      
 24 
     | 
    
         
            +
                    interface_name = "_#{class_name}"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  else
         
     | 
| 
      
 26 
     | 
    
         
            +
                    buf << "module #{namespace.join("::")}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                    indent += 1
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Using interface emulates a module we include to provide getters and setters
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # (thus making `super` possible)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  buf << "#{"  " * indent}interface #{interface_name}"
         
     | 
| 
      
 33 
     | 
    
         
            +
                  indent += 1
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  # Generating setters and getters for config attributes
         
     | 
| 
      
 36 
     | 
    
         
            +
                  config_attributes.each do |param|
         
     | 
| 
      
 37 
     | 
    
         
            +
                    type = coercion_mapping[param] || defaults[param.to_s]
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    type =
         
     | 
| 
      
 40 
     | 
    
         
            +
                      case type
         
     | 
| 
      
 41 
     | 
    
         
            +
                      in NilClass
         
     | 
| 
      
 42 
     | 
    
         
            +
                        "untyped"
         
     | 
| 
      
 43 
     | 
    
         
            +
                      in Symbol
         
     | 
| 
      
 44 
     | 
    
         
            +
                        TYPE_TO_CLASS.fetch(type) { defaults[param] ? "Symbol" : "untyped" }
         
     | 
| 
      
 45 
     | 
    
         
            +
                      in Array
         
     | 
| 
      
 46 
     | 
    
         
            +
                        "Array[untyped]"
         
     | 
| 
      
 47 
     | 
    
         
            +
                      in array:, type:, **nil
         
     | 
| 
      
 48 
     | 
    
         
            +
                        "Array[#{TYPE_TO_CLASS.fetch(type, "untyped")}]"
         
     | 
| 
      
 49 
     | 
    
         
            +
                      in Hash
         
     | 
| 
      
 50 
     | 
    
         
            +
                        "Hash[string,untyped]"
         
     | 
| 
      
 51 
     | 
    
         
            +
                      in TrueClass | FalseClass
         
     | 
| 
      
 52 
     | 
    
         
            +
                        "bool"
         
     | 
| 
      
 53 
     | 
    
         
            +
                      else
         
     | 
| 
      
 54 
     | 
    
         
            +
                        type.class.to_s
         
     | 
| 
      
 55 
     | 
    
         
            +
                      end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    getter_type = type
         
     | 
| 
      
 58 
     | 
    
         
            +
                    getter_type = "#{type}?" unless required_attributes.include?(param)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                    buf << "#{"  " * indent}def #{param}: () -> #{getter_type}"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    buf << "#{"  " * indent}def #{param}=: (#{type}) -> void"
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    if type == "bool" || type == "bool?"
         
     | 
| 
      
 64 
     | 
    
         
            +
                      buf << "#{"  " * indent}def #{param}?: () -> #{getter_type}"
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  indent -= 1
         
     | 
| 
      
 69 
     | 
    
         
            +
                  buf << "#{"  " * indent}end"
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  buf << ""
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  buf << "#{"  " * indent}class #{class_name} < #{superclass.name}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  indent += 1
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  buf << "#{"  " * indent}include #{interface_name}"
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  indent -= 1
         
     | 
| 
      
 79 
     | 
    
         
            +
                  buf << "#{"  " * indent}end"
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  unless namespace.empty?
         
     | 
| 
      
 82 
     | 
    
         
            +
                    buf << "end"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  buf << ""
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  buf.join("\n")
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
              Config.extend RBSGenerator
         
     | 
| 
      
 92 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/anyway/settings.rb
    CHANGED
    
    | 
         @@ -5,16 +5,66 @@ module Anyway 
     | 
|
| 
       5 
5 
     | 
    
         
             
              #
         
     | 
| 
       6 
6 
     | 
    
         
             
              # Settings contain the library-wide configuration.
         
     | 
| 
       7 
7 
     | 
    
         
             
              class Settings
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Future encapsulates settings that will be introduced in the upcoming version
         
     | 
| 
      
 9 
     | 
    
         
            +
                # with the default values, which could break compatibility
         
     | 
| 
      
 10 
     | 
    
         
            +
                class Future
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def setting(name, default_value)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      settings[name] = default_value
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                      define_method(name) do
         
     | 
| 
      
 16 
     | 
    
         
            +
                        store[name]
         
     | 
| 
      
 17 
     | 
    
         
            +
                      end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      define_method(:"#{name}=") do |val|
         
     | 
| 
      
 20 
     | 
    
         
            +
                        store[name] = val
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    def settings
         
     | 
| 
      
 25 
     | 
    
         
            +
                      @settings ||= {}
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @store = {}
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def use(*names)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    store.clear
         
     | 
| 
      
 35 
     | 
    
         
            +
                    names.each { store[_1] = self.class.settings[_1] }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  attr_reader :store
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
       8 
43 
     | 
    
         
             
                class << self
         
     | 
| 
       9 
44 
     | 
    
         
             
                  # Define whether to load data from
         
     | 
| 
       10 
45 
     | 
    
         
             
                  # *.yml.local (or credentials/local.yml.enc)
         
     | 
| 
       11 
46 
     | 
    
         
             
                  attr_accessor :use_local_files
         
     | 
| 
       12 
47 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                  #  
     | 
| 
       14 
     | 
    
         
            -
                   
     | 
| 
      
 48 
     | 
    
         
            +
                  # A proc returning a path to YML config file given the config name
         
     | 
| 
      
 49 
     | 
    
         
            +
                  attr_reader :default_config_path
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def default_config_path=(val)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    if val.is_a?(Proc)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      @default_config_path = val
         
     | 
| 
      
 54 
     | 
    
         
            +
                      return
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    val = val.to_s
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    @default_config_path = ->(name) { File.join(val, "#{name}.yml") }
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
       15 
61 
     | 
    
         | 
| 
       16 
62 
     | 
    
         
             
                  # Enable source tracing
         
     | 
| 
       17 
63 
     | 
    
         
             
                  attr_accessor :tracing_enabled
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def future
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @future ||= Future.new
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
       18 
68 
     | 
    
         
             
                end
         
     | 
| 
       19 
69 
     | 
    
         | 
| 
       20 
70 
     | 
    
         
             
                # By default, use local files only in development (that's the purpose if the local files)
         
     | 
    
        data/lib/anyway/tracing.rb
    CHANGED
    
    | 
         @@ -34,11 +34,11 @@ module Anyway 
     | 
|
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                  def record_value(val, *path, **opts)
         
     | 
| 
       36 
36 
     | 
    
         
             
                    key = path.pop
         
     | 
| 
       37 
     | 
    
         
            -
                    if val.is_a?(Hash)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    trace = if val.is_a?(Hash)
         
     | 
| 
       38 
38 
     | 
    
         
             
                      Trace.new.tap { _1.merge_values(val, **opts) }
         
     | 
| 
       39 
39 
     | 
    
         
             
                    else
         
     | 
| 
       40 
40 
     | 
    
         
             
                      Trace.new(:value, val, **opts)
         
     | 
| 
       41 
     | 
    
         
            -
                    end 
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                    target_trace = path.empty? ? self : value.dig(*path)
         
     | 
| 
       44 
44 
     | 
    
         
             
                    target_trace.value[key.to_s] = trace
         
     | 
| 
         @@ -86,7 +86,7 @@ module Anyway 
     | 
|
| 
       86 
86 
     | 
    
         
             
                    if trace?
         
     | 
| 
       87 
87 
     | 
    
         
             
                      value.transform_values(&:to_h).tap { _1.default_proc = nil }
         
     | 
| 
       88 
88 
     | 
    
         
             
                    else
         
     | 
| 
       89 
     | 
    
         
            -
                      {value 
     | 
| 
      
 89 
     | 
    
         
            +
                      {value, source}
         
     | 
| 
       90 
90 
     | 
    
         
             
                    end
         
     | 
| 
       91 
91 
     | 
    
         
             
                  end
         
     | 
| 
       92 
92 
     | 
    
         | 
| 
         @@ -141,7 +141,7 @@ module Anyway 
     | 
|
| 
       141 
141 
     | 
    
         | 
| 
       142 
142 
     | 
    
         
             
                  def current_trace() = trace_stack.last
         
     | 
| 
       143 
143 
     | 
    
         | 
| 
       144 
     | 
    
         
            -
                   
     | 
| 
      
 144 
     | 
    
         
            +
                  alias_method :tracing?, :current_trace
         
     | 
| 
       145 
145 
     | 
    
         | 
| 
       146 
146 
     | 
    
         
             
                  def source_stack
         
     | 
| 
       147 
147 
     | 
    
         
             
                    (Thread.current[:__anyway__trace_source_stack__] ||= [])
         
     | 
| 
         @@ -171,9 +171,9 @@ module Anyway 
     | 
|
| 
       171 
171 
     | 
    
         
             
                  return yield unless Tracing.tracing?
         
     | 
| 
       172 
172 
     | 
    
         
             
                  val = yield
         
     | 
| 
       173 
173 
     | 
    
         
             
                  if val.is_a?(Hash)
         
     | 
| 
       174 
     | 
    
         
            -
                    Tracing.current_trace.merge_values(val, type 
     | 
| 
      
 174 
     | 
    
         
            +
                    Tracing.current_trace.merge_values(val, type:, **opts)
         
     | 
| 
       175 
175 
     | 
    
         
             
                  else
         
     | 
| 
       176 
     | 
    
         
            -
                    Tracing.current_trace.record_value(val, *path, type 
     | 
| 
      
 176 
     | 
    
         
            +
                    Tracing.current_trace.record_value(val, *path, type:, **opts)
         
     | 
| 
       177 
177 
     | 
    
         
             
                  end
         
     | 
| 
       178 
178 
     | 
    
         
             
                  val
         
     | 
| 
       179 
179 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,134 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Anyway
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Contains a mapping between type IDs/names and deserializers
         
     | 
| 
      
 5 
     | 
    
         
            +
              class TypeRegistry
         
     | 
| 
      
 6 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def default
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @default ||= TypeRegistry.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @registry = {}
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def accept(name_or_object, &block)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  if !block && !name_or_object.respond_to?(:call)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    raise ArgumentError, "Please, provide a type casting block or an object implementing #call(val) method"
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  registry[name_or_object] = block || name_or_object
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def deserialize(raw, type_id, array: false)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  return if raw.nil?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  caster =
         
     | 
| 
      
 28 
     | 
    
         
            +
                    if type_id.is_a?(Symbol) || type_id.nil?
         
     | 
| 
      
 29 
     | 
    
         
            +
                      registry.fetch(type_id) { raise ArgumentError, "Unknown type: #{type_id}" }
         
     | 
| 
      
 30 
     | 
    
         
            +
                    else
         
     | 
| 
      
 31 
     | 
    
         
            +
                      raise ArgumentError, "Type must implement #call(val): #{type_id}" unless type_id.respond_to?(:call)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      type_id
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  if array
         
     | 
| 
      
 36 
     | 
    
         
            +
                    raw_arr = raw.is_a?(String) ? raw.split(/\s*,\s*/) : Array(raw)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    raw_arr.map { caster.call(_1) }
         
     | 
| 
      
 38 
     | 
    
         
            +
                  else
         
     | 
| 
      
 39 
     | 
    
         
            +
                    caster.call(raw)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def dup
         
     | 
| 
      
 44 
     | 
    
         
            +
                  new_obj = self.class.allocate
         
     | 
| 
      
 45 
     | 
    
         
            +
                  new_obj.instance_variable_set(:@registry, registry.dup)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  new_obj
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                attr_reader :registry
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              TypeRegistry.default.tap do |obj|
         
     | 
| 
      
 55 
     | 
    
         
            +
                obj.accept(nil, &:itself)
         
     | 
| 
      
 56 
     | 
    
         
            +
                obj.accept(:string, &:to_s)
         
     | 
| 
      
 57 
     | 
    
         
            +
                obj.accept(:integer, &:to_i)
         
     | 
| 
      
 58 
     | 
    
         
            +
                obj.accept(:float, &:to_f)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                obj.accept(:date) do
         
     | 
| 
      
 61 
     | 
    
         
            +
                  require "date" unless defined?(::Date)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  next _1 if _1.is_a?(::Date)
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  next _1.to_date if _1.respond_to?(:to_date)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  ::Date.parse(_1)
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                obj.accept(:datetime) do
         
     | 
| 
      
 71 
     | 
    
         
            +
                  require "date" unless defined?(::Date)
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  next _1 if _1.is_a?(::DateTime)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  next _1.to_datetime if _1.respond_to?(:to_datetime)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  ::DateTime.parse(_1)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                obj.accept(:uri) do
         
     | 
| 
      
 81 
     | 
    
         
            +
                  require "uri" unless defined?(::URI)
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  next _1 if _1.is_a?(::URI)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  ::URI.parse(_1)
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                obj.accept(:boolean) do
         
     | 
| 
      
 89 
     | 
    
         
            +
                  _1.to_s.match?(/\A(true|t|yes|y|1)\z/i)
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
              # TypeCaster is an object responsible for type-casting.
         
     | 
| 
      
 94 
     | 
    
         
            +
              # It uses a provided types registry and mapping, and also
         
     | 
| 
      
 95 
     | 
    
         
            +
              # accepts a fallback typecaster.
         
     | 
| 
      
 96 
     | 
    
         
            +
              class TypeCaster
         
     | 
| 
      
 97 
     | 
    
         
            +
                using Ext::DeepDup
         
     | 
| 
      
 98 
     | 
    
         
            +
                using Ext::Hash
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def initialize(mapping, registry: TypeRegistry.default, fallback: ::Anyway::AutoCast)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  @mapping = mapping.deep_dup
         
     | 
| 
      
 102 
     | 
    
         
            +
                  @registry = registry
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @fallback = fallback
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def coerce(key, val, config: mapping)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  caster_config = config[key.to_sym]
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  return fallback.coerce(key, val) unless caster_config
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  case caster_config
         
     | 
| 
      
 112 
     | 
    
         
            +
                  in array:, type:, **nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                    registry.deserialize(val, type, array: array)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  in Hash
         
     | 
| 
      
 115 
     | 
    
         
            +
                    return val unless val.is_a?(Hash)
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                    caster_config.each do |k, v|
         
     | 
| 
      
 118 
     | 
    
         
            +
                      ks = k.to_s
         
     | 
| 
      
 119 
     | 
    
         
            +
                      next unless val.key?(ks)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                      val[ks] = coerce(k, val[ks], config: caster_config)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    val
         
     | 
| 
      
 125 
     | 
    
         
            +
                  else
         
     | 
| 
      
 126 
     | 
    
         
            +
                    registry.deserialize(val, caster_config)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                private
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                attr_reader :mapping, :registry, :fallback
         
     | 
| 
      
 133 
     | 
    
         
            +
              end
         
     | 
| 
      
 134 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Anyway
         
     | 
| 
      
 4 
     | 
    
         
            +
              using Anyway::Ext::DeepDup
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              module Utils
         
     | 
| 
      
 7 
     | 
    
         
            +
                def self.deep_merge!(source, other)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  other.each do |key, other_value|
         
     | 
| 
      
 9 
     | 
    
         
            +
                    this_value = source[key]
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      deep_merge!(this_value, other_value)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    else
         
     | 
| 
      
 14 
     | 
    
         
            +
                      source[key] = other_value.deep_dup
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  source
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/anyway/version.rb
    CHANGED
    
    
    
        data/lib/anyway_config.rb
    CHANGED
    
    | 
         @@ -11,12 +11,16 @@ require "anyway/ext/deep_dup" 
     | 
|
| 
       11 
11 
     | 
    
         
             
            require "anyway/ext/deep_freeze"
         
     | 
| 
       12 
12 
     | 
    
         
             
            require "anyway/ext/hash"
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
            require "anyway/utils/deep_merge"
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       14 
16 
     | 
    
         
             
            require "anyway/settings"
         
     | 
| 
       15 
17 
     | 
    
         
             
            require "anyway/tracing"
         
     | 
| 
       16 
18 
     | 
    
         
             
            require "anyway/config"
         
     | 
| 
       17 
19 
     | 
    
         
             
            require "anyway/auto_cast"
         
     | 
| 
      
 20 
     | 
    
         
            +
            require "anyway/type_casting"
         
     | 
| 
       18 
21 
     | 
    
         
             
            require "anyway/env"
         
     | 
| 
       19 
22 
     | 
    
         
             
            require "anyway/loaders"
         
     | 
| 
      
 23 
     | 
    
         
            +
            require "anyway/rbs"
         
     | 
| 
       20 
24 
     | 
    
         | 
| 
       21 
25 
     | 
    
         
             
            module Anyway # :nodoc:
         
     | 
| 
       22 
26 
     | 
    
         
             
              class << self
         
     |