anyway_config 2.1.0 → 2.2.0

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.
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
@@ -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 => trace
41
+ end
42
42
 
43
43
  target_trace = path.empty? ? self : value.dig(*path)
44
44
  target_trace.value[key.to_s] = trace
@@ -0,0 +1,121 @@
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
+ caster =
26
+ if type_id.is_a?(Symbol)
27
+ registry.fetch(type_id) { raise ArgumentError, "Unknown type: #{type_id}" }
28
+ else
29
+ raise ArgumentError, "Type must implement #call(val): #{type_id}" unless type_id.respond_to?(:call)
30
+ type_id
31
+ end
32
+
33
+ if array
34
+ raw_arr = raw.is_a?(Array) ? raw : raw.split(/\s*,\s*/)
35
+ raw_arr.map { caster.call(_1) }
36
+ else
37
+ caster.call(raw)
38
+ end
39
+ end
40
+
41
+ def dup
42
+ new_obj = self.class.allocate
43
+ new_obj.instance_variable_set(:@registry, registry.dup)
44
+ new_obj
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :registry
50
+ end
51
+
52
+ TypeRegistry.default.tap do |obj|
53
+ obj.accept(:string, &:to_s)
54
+ obj.accept(:integer, &:to_i)
55
+ obj.accept(:float, &:to_f)
56
+
57
+ obj.accept(:date) do
58
+ require "date" unless defined?(::Date)
59
+
60
+ Date.parse(_1)
61
+ end
62
+
63
+ obj.accept(:datetime) do
64
+ require "date" unless defined?(::Date)
65
+
66
+ DateTime.parse(_1)
67
+ end
68
+
69
+ obj.accept(:uri) do
70
+ require "uri" unless defined?(::URI)
71
+
72
+ URI.parse(_1)
73
+ end
74
+
75
+ obj.accept(:boolean) do
76
+ _1.match?(/\A(true|t|yes|y|1)\z/i)
77
+ end
78
+ end
79
+
80
+ # TypeCaster is an object responsible for type-casting.
81
+ # It uses a provided types registry and mapping, and also
82
+ # accepts a fallback typecaster.
83
+ class TypeCaster
84
+ using Ext::DeepDup
85
+ using Ext::Hash
86
+
87
+ def initialize(mapping, registry: TypeRegistry.default, fallback: ::Anyway::AutoCast)
88
+ @mapping = mapping.deep_dup
89
+ @registry = registry
90
+ @fallback = fallback
91
+ end
92
+
93
+ def coerce(key, val, config: mapping)
94
+ caster_config = config[key.to_sym]
95
+
96
+ return fallback.coerce(key, val) unless caster_config
97
+
98
+ case caster_config
99
+ in array:, type:, **nil
100
+ registry.deserialize(val, type, array: array)
101
+ in Hash
102
+ return val unless val.is_a?(Hash)
103
+
104
+ caster_config.each do |k, v|
105
+ ks = k.to_s
106
+ next unless val.key?(ks)
107
+
108
+ val[ks] = coerce(k, val[ks], config: caster_config)
109
+ end
110
+
111
+ val
112
+ else
113
+ registry.deserialize(val, caster_config)
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ attr_reader :mapping, :registry, :fallback
120
+ end
121
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anyway # :nodoc:
4
- VERSION = "2.1.0"
4
+ VERSION = "2.2.0"
5
5
  end
data/lib/anyway_config.rb CHANGED
@@ -17,8 +17,10 @@ require "anyway/settings"
17
17
  require "anyway/tracing"
18
18
  require "anyway/config"
19
19
  require "anyway/auto_cast"
20
+ require "anyway/type_casting"
20
21
  require "anyway/env"
21
22
  require "anyway/loaders"
23
+ require "anyway/rbs"
22
24
 
23
25
  module Anyway # :nodoc:
24
26
  class << self
@@ -0,0 +1,123 @@
1
+ module Anyway
2
+ def self.env: -> Env
3
+ def self.loaders: -> Loaders::Registry
4
+
5
+ class Settings
6
+ def self.default_config_path=: (^(untyped) -> String val) -> ^(untyped) -> String?
7
+ def self.future: -> Future
8
+
9
+ class Future
10
+ def self.setting: (untyped name, untyped default_value) -> untyped
11
+ def self.settings: -> Hash[untyped, untyped]
12
+ def use: (*untyped names) -> untyped
13
+ end
14
+ end
15
+
16
+ module Tracing
17
+ class Trace
18
+ def merge!: (Trace another_trace) -> void
19
+ end
20
+
21
+ def inspect: -> String
22
+ def self.capture: ?{ -> Hash[untyped, untyped] } -> nil
23
+ def self.trace_stack: -> Array[untyped]
24
+ def self.current_trace: -> Trace?
25
+ def self.source_stack: -> Array[untyped]
26
+ def self.current_trace_source: -> {type: :accessor, called_from: untyped}
27
+ def self.with_trace_source: (untyped src) -> untyped
28
+ def trace!: (Symbol, *Array[String] paths, **untyped) ?{ -> Hash[untyped, untyped]} -> Hash[untyped, untyped]
29
+ def self.trace!: (Symbol, *Array[String] paths, **untyped) ?{ -> Hash[untyped, untyped]} -> Hash[untyped, untyped]
30
+ end
31
+
32
+ module RBSGenerator
33
+ def to_rbs: -> String
34
+ end
35
+
36
+ module OptparseConfig
37
+ def option_parser: -> OptionParser
38
+ def parse_options!: (Array[String]) -> void
39
+
40
+ module ClassMethods
41
+ def ignore_options: (*Symbol args) -> void
42
+ def describe_options: (**(String | {desc: String, type: Module})) -> void
43
+ def flag_options: (*Symbol args) -> void
44
+ def extend_options: { (OptionParser, Config) -> void } -> void
45
+ end
46
+ end
47
+
48
+ module DynamicConfig
49
+ module ClassMethods
50
+ def for: (String | Symbol name, ?auto_cast: bool, **untyped) -> Hash[untyped, untyped]
51
+ end
52
+ end
53
+
54
+ class Config
55
+ extend RBSGenerator
56
+ extend DynamicConfig::ClassMethods
57
+ extend OptparseConfig::ClassMethods
58
+ include DynamicConfig
59
+ include OptparseConfig
60
+
61
+ def self.attr_config: (*Symbol args, **untyped) -> void
62
+ def self.defaults: -> Hash[String, untyped]
63
+ def self.config_attributes: -> Array[Symbol]?
64
+ def self.required: (*Symbol names) -> void
65
+ def self.required_attributes: -> Array[Symbol]
66
+ def self.on_load: (*Symbol callbacks) ?{ () -> void } -> void
67
+ def self.config_name: (?(Symbol | String) val) -> String?
68
+ def self.env_prefix: (?(Symbol | String) val) -> String
69
+ def self.coerce_types: (untyped mapping) -> untyped
70
+ def self.coercion_mapping: -> Hash[untyped, untyped]?
71
+ def self.disable_auto_cast!: -> void
72
+
73
+ attr_reader config_name: String
74
+ attr_reader env_prefix: String
75
+
76
+ def initialize: (?Hash[Symbol | String, untyped] overrides) -> void
77
+ def reload: (?Hash[Symbol | String, untyped] overrides) -> Config
78
+ def clear: -> void
79
+ def load: (Hash[Symbol | String, untyped] overrides) -> Config
80
+ def dig: (*(Symbol | String) keys) -> untyped
81
+ def to_h: -> Hash[untyped, untyped]
82
+ def dup: -> Config
83
+ def deconstruct_keys: (untyped keys) -> Hash[untyped, untyped]
84
+ def to_source_trace: -> Hash[String, untyped]
85
+ def inspect: -> String
86
+ def pretty_print: (untyped q) -> untyped
87
+
88
+ private
89
+ attr_reader values: Hash[untyped, untyped]
90
+ def raise_validation_error: (String msg) -> void
91
+
92
+ class Error < StandardError
93
+ end
94
+
95
+ class ValidationError < Error
96
+ end
97
+ end
98
+
99
+ class Env
100
+ def clear: -> void
101
+ def fetch: (String prefix) -> untyped
102
+ def fetch_with_trace: (String prefix) -> [untyped, Tracing::Trace?]
103
+ end
104
+
105
+ module Loaders
106
+ class Base
107
+ include Tracing
108
+
109
+ def self.call: (?local: bool, **untyped) -> untyped
110
+ def initialize: (local: bool) -> void
111
+ def use_local?: -> bool
112
+ end
113
+
114
+ class Registry
115
+ def prepend: (Symbol id, Base loader) -> void
116
+ def append: (Symbol id, Base loader) -> void
117
+ def insert_before: (Symbol another_id, Symbol id, Base loader) -> void
118
+ def insert_after: (Symbol another_id, Symbol id, Base loader) -> void
119
+ def override: (Symbol id, Base loader) -> void
120
+ def delete: (Symbol id) -> void
121
+ end
122
+ end
123
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anyway_config
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-28 00:00:00.000000000 Z
11
+ date: 2021-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-next-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.11.0
19
+ version: 0.13.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.11.0
26
+ version: 0.13.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: ammeter
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.8'
97
+ - !ruby/object:Gem::Dependency
98
+ name: steep
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: "\n Configuration DSL for Ruby libraries and applications.\n Allows
98
112
  you to easily follow the twelve-factor application principles (https://12factor.net/config).\n
99
113
  \ "
@@ -113,10 +127,12 @@ files:
113
127
  - lib/.rbnext/1995.next/anyway/tracing.rb
114
128
  - lib/.rbnext/2.7/anyway/auto_cast.rb
115
129
  - lib/.rbnext/2.7/anyway/config.rb
116
- - lib/.rbnext/2.7/anyway/option_parser_builder.rb
117
130
  - lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb
131
+ - lib/.rbnext/2.7/anyway/rbs.rb
118
132
  - lib/.rbnext/2.7/anyway/settings.rb
119
133
  - lib/.rbnext/2.7/anyway/tracing.rb
134
+ - lib/.rbnext/2.7/anyway/type_casting.rb
135
+ - lib/.rbnext/3.0/anyway/auto_cast.rb
120
136
  - lib/.rbnext/3.0/anyway/config.rb
121
137
  - lib/.rbnext/3.0/anyway/loaders.rb
122
138
  - lib/.rbnext/3.0/anyway/loaders/base.rb
@@ -143,10 +159,12 @@ files:
143
159
  - lib/anyway/rails/loaders/yaml.rb
144
160
  - lib/anyway/rails/settings.rb
145
161
  - lib/anyway/railtie.rb
162
+ - lib/anyway/rbs.rb
146
163
  - lib/anyway/settings.rb
147
164
  - lib/anyway/testing.rb
148
165
  - lib/anyway/testing/helpers.rb
149
166
  - lib/anyway/tracing.rb
167
+ - lib/anyway/type_casting.rb
150
168
  - lib/anyway/utils/deep_merge.rb
151
169
  - lib/anyway/version.rb
152
170
  - lib/anyway_config.rb
@@ -159,6 +177,7 @@ files:
159
177
  - lib/generators/anyway/install/USAGE
160
178
  - lib/generators/anyway/install/install_generator.rb
161
179
  - lib/generators/anyway/install/templates/application_config.rb.tt
180
+ - sig/anyway_config.rbs
162
181
  homepage: http://github.com/palkan/anyway_config
163
182
  licenses:
164
183
  - MIT
@@ -168,7 +187,7 @@ metadata:
168
187
  documentation_uri: http://github.com/palkan/anyway_config
169
188
  homepage_uri: http://github.com/palkan/anyway_config
170
189
  source_code_uri: http://github.com/palkan/anyway_config
171
- post_install_message:
190
+ post_install_message:
172
191
  rdoc_options: []
173
192
  require_paths:
174
193
  - lib
@@ -183,8 +202,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
202
  - !ruby/object:Gem::Version
184
203
  version: '0'
185
204
  requirements: []
186
- rubygems_version: 3.0.6
187
- signing_key:
205
+ rubygems_version: 3.2.22
206
+ signing_key:
188
207
  specification_version: 4
189
208
  summary: Configuration DSL for Ruby libraries and applications
190
209
  test_files: []
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "optparse"
4
-
5
- module Anyway # :nodoc:
6
- # Initializes the OptionParser instance using the given configuration
7
- class OptionParserBuilder
8
- class << self
9
- def call(options)
10
- OptionParser.new do |opts|
11
- opts.accept(AutoCast) { |_1| AutoCast.call(_1) }
12
-
13
- options.each do |key, descriptor|
14
- opts.on(*option_parser_on_args(key, **descriptor)) do |val|
15
- yield [key, val]
16
- end
17
- end
18
- end
19
- end
20
-
21
- private
22
-
23
- def option_parser_on_args(key, flag: false, desc: nil, type: AutoCast)
24
- on_args = ["--#{key.to_s.tr("_", "-")}#{flag ? "" : " VALUE"}"]
25
- on_args << type unless flag
26
- on_args << desc unless desc.nil?
27
- on_args
28
- end
29
- end
30
- end
31
- end