anyway_config 2.1.0 → 2.2.0

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