optify-config 1.17.4 → 1.17.9
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/ext/optify_ruby/extconf.rb +1 -1
- data/ext/optify_ruby/src/lib.rs +163 -1
- data/lib/optify.rb +1 -1
- data/lib/optify_ruby/base_config.rb +3 -144
- data/lib/optify_ruby/get_options_preferences.rb +1 -1
- data/lib/optify_ruby/implementation.rb +2 -2
- data/lib/optify_ruby/options_metadata.rb +4 -2
- data/lib/optify_ruby/provider_module.rb +23 -26
- data/lib/optify_ruby/watcher_implementation.rb +1 -1
- data/rbi/optify.rbi +33 -27
- data/sig/optify.rbs +15 -21
- metadata +45 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd28c71634f29bbb3173bbade43163012a089e7d1e54aa4fbfeedace176580ea
|
|
4
|
+
data.tar.gz: 9e732995e66a00e817f9fe47fb2950deb6fe4ab0768553815a0cc13ce6eeab6d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5a5d7a84b653bb3c66768f8b387fdad3151ceae3d64467adb5d2f86b5f60f3f627cd0881d904ca358ef14878016984868cc0c2c9cee601dc48e2d20ff269b003
|
|
7
|
+
data.tar.gz: 07fa10d2c22ceac357f9a3e8fcf714565a103bfb12e097e3834ecf3e09ef4a4c0d5c03303f639f105e92ed9be89613e094d1141a7f88c3137a4492dc73d7b0bb
|
data/ext/optify_ruby/extconf.rb
CHANGED
data/ext/optify_ruby/src/lib.rs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use magnus::{function, method, prelude::*, wrap, Object, Ruby};
|
|
1
|
+
use magnus::{function, method, prelude::*, wrap, Object, Ruby, Value as RbValue};
|
|
2
2
|
use optify::builder::OptionsProviderBuilder;
|
|
3
3
|
use optify::builder::OptionsRegistryBuilder;
|
|
4
4
|
use optify::builder::OptionsWatcherBuilder;
|
|
@@ -13,6 +13,40 @@ use crate::preferences::MutGetOptionsPreferences;
|
|
|
13
13
|
|
|
14
14
|
mod preferences;
|
|
15
15
|
|
|
16
|
+
fn json_value_to_ruby(ruby: &Ruby, value: &serde_json::Value) -> Result<RbValue, magnus::Error> {
|
|
17
|
+
match value {
|
|
18
|
+
serde_json::Value::Null => Ok(ruby.qnil().as_value()),
|
|
19
|
+
serde_json::Value::Bool(b) => Ok(ruby.into_value(*b)),
|
|
20
|
+
serde_json::Value::Number(n) => {
|
|
21
|
+
if let Some(i) = n.as_i64() {
|
|
22
|
+
Ok(ruby.into_value(i))
|
|
23
|
+
} else if let Some(f) = n.as_f64() {
|
|
24
|
+
Ok(ruby.into_value(f))
|
|
25
|
+
} else {
|
|
26
|
+
Err(magnus::Error::new(
|
|
27
|
+
ruby.exception_type_error(),
|
|
28
|
+
"Invalid number",
|
|
29
|
+
))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
serde_json::Value::String(s) => Ok(ruby.into_value(s.as_str())),
|
|
33
|
+
serde_json::Value::Array(arr) => {
|
|
34
|
+
let rb_arr = ruby.ary_new_capa(arr.len());
|
|
35
|
+
for item in arr {
|
|
36
|
+
rb_arr.push(json_value_to_ruby(ruby, item)?)?;
|
|
37
|
+
}
|
|
38
|
+
Ok(rb_arr.as_value())
|
|
39
|
+
}
|
|
40
|
+
serde_json::Value::Object(obj) => {
|
|
41
|
+
let rb_hash = ruby.hash_new_capa(obj.len());
|
|
42
|
+
for (key, val) in obj {
|
|
43
|
+
rb_hash.aset(key.as_str(), json_value_to_ruby(ruby, val)?)?;
|
|
44
|
+
}
|
|
45
|
+
Ok(rb_hash.as_value())
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
16
50
|
#[wrap(class = "Optify::OptionsProvider")]
|
|
17
51
|
struct WrappedOptionsProvider(RefCell<OptionsProvider>);
|
|
18
52
|
|
|
@@ -87,6 +121,23 @@ impl WrappedOptionsProvider {
|
|
|
87
121
|
}
|
|
88
122
|
}
|
|
89
123
|
|
|
124
|
+
fn get_all_options_hash(
|
|
125
|
+
ruby: &Ruby,
|
|
126
|
+
rb_self: &Self,
|
|
127
|
+
feature_names: Vec<String>,
|
|
128
|
+
preferences: &MutGetOptionsPreferences,
|
|
129
|
+
) -> Result<RbValue, magnus::Error> {
|
|
130
|
+
let preferences = &convert_preferences(preferences);
|
|
131
|
+
match rb_self
|
|
132
|
+
.0
|
|
133
|
+
.borrow()
|
|
134
|
+
.get_all_options(&feature_names, None, Some(preferences))
|
|
135
|
+
{
|
|
136
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
137
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
90
141
|
fn get_canonical_feature_name(
|
|
91
142
|
ruby: &Ruby,
|
|
92
143
|
rb_self: &Self,
|
|
@@ -179,6 +230,41 @@ impl WrappedOptionsProvider {
|
|
|
179
230
|
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
180
231
|
}
|
|
181
232
|
}
|
|
233
|
+
|
|
234
|
+
fn get_options_hash(
|
|
235
|
+
ruby: &Ruby,
|
|
236
|
+
rb_self: &Self,
|
|
237
|
+
key: String,
|
|
238
|
+
feature_names: Vec<String>,
|
|
239
|
+
) -> Result<RbValue, magnus::Error> {
|
|
240
|
+
match rb_self
|
|
241
|
+
.0
|
|
242
|
+
.borrow()
|
|
243
|
+
.get_options_with_preferences(&key, &feature_names, None, None)
|
|
244
|
+
{
|
|
245
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
246
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
fn get_options_hash_with_preferences(
|
|
251
|
+
ruby: &Ruby,
|
|
252
|
+
rb_self: &Self,
|
|
253
|
+
key: String,
|
|
254
|
+
feature_names: Vec<String>,
|
|
255
|
+
preferences: &MutGetOptionsPreferences,
|
|
256
|
+
) -> Result<RbValue, magnus::Error> {
|
|
257
|
+
let preferences = &convert_preferences(preferences);
|
|
258
|
+
match rb_self.0.borrow().get_options_with_preferences(
|
|
259
|
+
&key,
|
|
260
|
+
&feature_names,
|
|
261
|
+
None,
|
|
262
|
+
Some(preferences),
|
|
263
|
+
) {
|
|
264
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
265
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
266
|
+
}
|
|
267
|
+
}
|
|
182
268
|
}
|
|
183
269
|
|
|
184
270
|
#[derive(Clone)]
|
|
@@ -277,6 +363,23 @@ impl WrappedOptionsWatcher {
|
|
|
277
363
|
}
|
|
278
364
|
}
|
|
279
365
|
|
|
366
|
+
fn get_all_options_hash(
|
|
367
|
+
ruby: &Ruby,
|
|
368
|
+
rb_self: &Self,
|
|
369
|
+
feature_names: Vec<String>,
|
|
370
|
+
preferences: &MutGetOptionsPreferences,
|
|
371
|
+
) -> Result<RbValue, magnus::Error> {
|
|
372
|
+
let preferences = &convert_preferences(preferences);
|
|
373
|
+
match rb_self
|
|
374
|
+
.0
|
|
375
|
+
.borrow()
|
|
376
|
+
.get_all_options(&feature_names, None, Some(preferences))
|
|
377
|
+
{
|
|
378
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
379
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
280
383
|
fn get_canonical_feature_name(
|
|
281
384
|
ruby: &Ruby,
|
|
282
385
|
rb_self: &Self,
|
|
@@ -368,6 +471,41 @@ impl WrappedOptionsWatcher {
|
|
|
368
471
|
}
|
|
369
472
|
}
|
|
370
473
|
|
|
474
|
+
fn get_options_hash(
|
|
475
|
+
ruby: &Ruby,
|
|
476
|
+
rb_self: &Self,
|
|
477
|
+
key: String,
|
|
478
|
+
feature_names: Vec<String>,
|
|
479
|
+
) -> Result<RbValue, magnus::Error> {
|
|
480
|
+
match rb_self
|
|
481
|
+
.0
|
|
482
|
+
.borrow()
|
|
483
|
+
.get_options_with_preferences(&key, &feature_names, None, None)
|
|
484
|
+
{
|
|
485
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
486
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
fn get_options_hash_with_preferences(
|
|
491
|
+
ruby: &Ruby,
|
|
492
|
+
rb_self: &Self,
|
|
493
|
+
key: String,
|
|
494
|
+
feature_names: Vec<String>,
|
|
495
|
+
preferences: &MutGetOptionsPreferences,
|
|
496
|
+
) -> Result<RbValue, magnus::Error> {
|
|
497
|
+
let preferences = &convert_preferences(preferences);
|
|
498
|
+
match rb_self.0.borrow().get_options_with_preferences(
|
|
499
|
+
&key,
|
|
500
|
+
&feature_names,
|
|
501
|
+
None,
|
|
502
|
+
Some(preferences),
|
|
503
|
+
) {
|
|
504
|
+
Ok(options) => json_value_to_ruby(ruby, &options),
|
|
505
|
+
Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
371
509
|
fn last_modified(&self) -> std::time::SystemTime {
|
|
372
510
|
self.0.borrow().last_modified()
|
|
373
511
|
}
|
|
@@ -442,6 +580,10 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
|
|
|
442
580
|
"get_all_options_json",
|
|
443
581
|
method!(WrappedOptionsProvider::get_all_options_json, 2),
|
|
444
582
|
)?;
|
|
583
|
+
provider_class.define_method(
|
|
584
|
+
"get_all_options_hash",
|
|
585
|
+
method!(WrappedOptionsProvider::get_all_options_hash, 2),
|
|
586
|
+
)?;
|
|
445
587
|
provider_class.define_method(
|
|
446
588
|
"get_canonical_feature_name",
|
|
447
589
|
method!(WrappedOptionsProvider::get_canonical_feature_name, 1),
|
|
@@ -458,6 +600,14 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
|
|
|
458
600
|
"get_options_json_with_preferences",
|
|
459
601
|
method!(WrappedOptionsProvider::get_options_json_with_preferences, 3),
|
|
460
602
|
)?;
|
|
603
|
+
provider_class.define_method(
|
|
604
|
+
"get_options_hash",
|
|
605
|
+
method!(WrappedOptionsProvider::get_options_hash, 2),
|
|
606
|
+
)?;
|
|
607
|
+
provider_class.define_method(
|
|
608
|
+
"get_options_hash_with_preferences",
|
|
609
|
+
method!(WrappedOptionsProvider::get_options_hash_with_preferences, 3),
|
|
610
|
+
)?;
|
|
461
611
|
|
|
462
612
|
// Private methods for internal use.
|
|
463
613
|
provider_class.define_private_method(
|
|
@@ -561,6 +711,10 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
|
|
|
561
711
|
"get_all_options_json",
|
|
562
712
|
method!(WrappedOptionsWatcher::get_all_options_json, 2),
|
|
563
713
|
)?;
|
|
714
|
+
watcher_class.define_method(
|
|
715
|
+
"get_all_options_hash",
|
|
716
|
+
method!(WrappedOptionsWatcher::get_all_options_hash, 2),
|
|
717
|
+
)?;
|
|
564
718
|
watcher_class.define_method(
|
|
565
719
|
"get_canonical_feature_name",
|
|
566
720
|
method!(WrappedOptionsWatcher::get_canonical_feature_name, 1),
|
|
@@ -577,6 +731,14 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
|
|
|
577
731
|
"get_options_json_with_preferences",
|
|
578
732
|
method!(WrappedOptionsWatcher::get_options_json_with_preferences, 3),
|
|
579
733
|
)?;
|
|
734
|
+
watcher_class.define_method(
|
|
735
|
+
"get_options_hash",
|
|
736
|
+
method!(WrappedOptionsWatcher::get_options_hash, 2),
|
|
737
|
+
)?;
|
|
738
|
+
watcher_class.define_method(
|
|
739
|
+
"get_options_hash_with_preferences",
|
|
740
|
+
method!(WrappedOptionsWatcher::get_options_hash_with_preferences, 3),
|
|
741
|
+
)?;
|
|
580
742
|
watcher_class.define_method(
|
|
581
743
|
"last_modified",
|
|
582
744
|
method!(WrappedOptionsWatcher::last_modified, 0),
|
data/lib/optify.rb
CHANGED
|
@@ -1,159 +1,18 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'optify-from_hash'
|
|
4
5
|
require 'sorbet-runtime'
|
|
5
6
|
require 'tapioca'
|
|
6
7
|
|
|
7
8
|
module Optify
|
|
9
|
+
# DEPRECATED: Use `Optify::FromHashable` instead.
|
|
8
10
|
# A base class for classes from configuration files.
|
|
9
11
|
# Classes that derive from this can easily be used with `Optify::OptionsProvider.get_options`
|
|
10
12
|
# because they will have an implementation of `from_hash` that works recursively.
|
|
11
13
|
# This class is a work in progress with minimal error handling.
|
|
12
14
|
# It may be moved to another gem in the future.
|
|
13
|
-
class BaseConfig
|
|
14
|
-
extend T::Sig
|
|
15
|
-
extend T::Helpers
|
|
15
|
+
class BaseConfig < Optify::FromHashable
|
|
16
16
|
abstract!
|
|
17
|
-
|
|
18
|
-
# Create a new immutable instance of the class from a hash.
|
|
19
|
-
#
|
|
20
|
-
# This is a class method so that it can set members with private setters.
|
|
21
|
-
# @param hash The hash to create the instance from.
|
|
22
|
-
# @return The new instance.
|
|
23
|
-
#: (Hash[untyped, untyped] hash) -> instance
|
|
24
|
-
def self.from_hash(hash)
|
|
25
|
-
instance = new
|
|
26
|
-
|
|
27
|
-
hash.each do |key, value|
|
|
28
|
-
begin
|
|
29
|
-
method = instance_method(key)
|
|
30
|
-
rescue StandardError
|
|
31
|
-
raise ArgumentError, "Error converting hash to `#{name}` because of key \"#{key}\". Perhaps \"#{key}\" is not a valid attribute for `#{name}`."
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
sig = T::Utils.signature_for_method(method)
|
|
35
|
-
raise "A Sorbet signature is required for `#{name}.#{key}`." if sig.nil?
|
|
36
|
-
|
|
37
|
-
sig_return_type = sig.return_type
|
|
38
|
-
value = _convert_value(value, sig_return_type)
|
|
39
|
-
instance.instance_variable_set("@#{key}", value)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
instance.freeze
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
#: (untyped value, untyped type) -> untyped
|
|
46
|
-
def self._convert_value(value, type)
|
|
47
|
-
if type.is_a?(T::Types::Untyped)
|
|
48
|
-
# No preferred type is given, so return the value as is.
|
|
49
|
-
return value
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
return value.to_sym if type.is_a?(T::Types::Simple) && type.raw_type == Symbol
|
|
53
|
-
|
|
54
|
-
case value
|
|
55
|
-
when Array
|
|
56
|
-
# Handle `T.nilable(T::Array[...])`
|
|
57
|
-
type = type.unwrap_nilable if type.respond_to?(:unwrap_nilable)
|
|
58
|
-
inner_type = type.type
|
|
59
|
-
return value.map { |v| _convert_value(v, inner_type) }.freeze
|
|
60
|
-
when Hash
|
|
61
|
-
# Handle `T.nilable(T::Hash[...])` and `T.any(...)`.
|
|
62
|
-
# We used to use `type = type.unwrap_nilable if type.respond_to?(:unwrap_nilable)`, but it's not needed now that we handle `T.any(...)`
|
|
63
|
-
# because using `.types` works for both cases.
|
|
64
|
-
if type.respond_to?(:types)
|
|
65
|
-
# Find a type that works for the hash.
|
|
66
|
-
type.types.each do |t|
|
|
67
|
-
return _convert_hash(value, t).freeze
|
|
68
|
-
rescue StandardError
|
|
69
|
-
# Ignore and try the next type.
|
|
70
|
-
end
|
|
71
|
-
raise TypeError, "Could not convert hash: #{value} to #{type}."
|
|
72
|
-
end
|
|
73
|
-
return _convert_hash(value, type).freeze
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# It would be nice to validate that the value is of the correct type here.
|
|
77
|
-
# For example that a string is a string and an Integer is an Integer.
|
|
78
|
-
value
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
#: (Hash[untyped, untyped] hash, untyped type) -> untyped
|
|
82
|
-
def self._convert_hash(hash, type)
|
|
83
|
-
if type.respond_to?(:raw_type)
|
|
84
|
-
# There is an object for the hash.
|
|
85
|
-
# It could be a custom class, a String, or maybe something else.
|
|
86
|
-
type_for_hash = type.raw_type
|
|
87
|
-
return type_for_hash.from_hash(hash) if type_for_hash.respond_to?(:from_hash)
|
|
88
|
-
elsif type.is_a?(T::Types::TypedHash)
|
|
89
|
-
# The hash should be a hash, but the values might be objects to convert.
|
|
90
|
-
type_for_keys = type.keys
|
|
91
|
-
|
|
92
|
-
convert_key = if type_for_keys.is_a?(T::Types::Simple) && type_for_keys.raw_type == Symbol
|
|
93
|
-
lambda(&:to_sym)
|
|
94
|
-
else
|
|
95
|
-
lambda(&:itself)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
type_for_values = type.values
|
|
99
|
-
return hash.map { |k, v| [convert_key.call(k), _convert_value(v, type_for_values)] }.to_h
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
raise TypeError, "Could not convert hash #{hash} to `#{type}`."
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
private_class_method :_convert_hash, :_convert_value
|
|
106
|
-
|
|
107
|
-
# Compare this object with another object for equality.
|
|
108
|
-
# @param other The object to compare.
|
|
109
|
-
# @return [Boolean] true if the objects are equal; otherwise, false.
|
|
110
|
-
#: (untyped other) -> bool
|
|
111
|
-
def ==(other)
|
|
112
|
-
return true if other.equal?(self)
|
|
113
|
-
return false unless other.is_a?(self.class)
|
|
114
|
-
|
|
115
|
-
instance_variables.all? do |name|
|
|
116
|
-
instance_variable_get(name) == other.instance_variable_get(name)
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Convert this object to a Hash recursively.
|
|
121
|
-
# This is mostly the reverse operation of `from_hash`,
|
|
122
|
-
# as keys will be symbols
|
|
123
|
-
# and `from_hash` will convert strings to symbols if that's how the attribute is declared.
|
|
124
|
-
# @return [Hash] The hash representation of this object.
|
|
125
|
-
#: () -> Hash[Symbol, untyped]
|
|
126
|
-
def to_h
|
|
127
|
-
result = Hash.new(instance_variables.size)
|
|
128
|
-
|
|
129
|
-
instance_variables.each do |var_name|
|
|
130
|
-
# Remove the @ prefix to get the method name
|
|
131
|
-
method_name = var_name.to_s[1..] #: as !nil
|
|
132
|
-
value = instance_variable_get(var_name)
|
|
133
|
-
result[method_name.to_sym] = _convert_value_to_hash(value)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
result
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
private
|
|
140
|
-
|
|
141
|
-
#: (untyped value) -> untyped
|
|
142
|
-
def _convert_value_to_hash(value)
|
|
143
|
-
case value
|
|
144
|
-
when Array
|
|
145
|
-
value.map { |v| _convert_value_to_hash(v) }
|
|
146
|
-
when Hash
|
|
147
|
-
value.transform_values { |v| _convert_value_to_hash(v) }
|
|
148
|
-
when nil
|
|
149
|
-
nil
|
|
150
|
-
else
|
|
151
|
-
if value.respond_to?(:to_h)
|
|
152
|
-
value.to_h
|
|
153
|
-
else
|
|
154
|
-
value
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
17
|
end
|
|
159
18
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'sorbet-runtime'
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ require_relative './provider_module'
|
|
|
11
11
|
module Optify
|
|
12
12
|
# Options for caching.
|
|
13
13
|
# Only enabling or disabling caching is supported for now.
|
|
14
|
-
class CacheOptions <
|
|
14
|
+
class CacheOptions < FromHashable
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Provides configurations based on keys and enabled feature names.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'sorbet-runtime'
|
|
5
5
|
|
|
@@ -7,7 +7,9 @@ require_relative './base_config'
|
|
|
7
7
|
|
|
8
8
|
module Optify
|
|
9
9
|
# Information about a feature.
|
|
10
|
-
class OptionsMetadata <
|
|
10
|
+
class OptionsMetadata < FromHashable
|
|
11
|
+
extend T::Sig
|
|
12
|
+
|
|
11
13
|
sig { returns(T.nilable(T::Array[String])) }
|
|
12
14
|
attr_reader :aliases
|
|
13
15
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'json'
|
|
4
5
|
require 'sorbet-runtime'
|
|
5
6
|
|
|
6
7
|
module Optify
|
|
@@ -48,11 +49,11 @@ module Optify
|
|
|
48
49
|
# @param feature_names The enabled feature names to use to build the options.
|
|
49
50
|
# @param config_class The class of the configuration to return.
|
|
50
51
|
# The class must implement `from_hash` as a class method to convert a hash to an instance of the class.
|
|
51
|
-
# It is recommended to use a class that extends `Optify::
|
|
52
|
+
# It is recommended to use a class that extends `Optify::FromHashable` because it implements `from_hash`.
|
|
52
53
|
# @param cache_options Set this if caching is desired. Only very simple caching is supported for now.
|
|
53
54
|
# @param preferences The preferences to use when getting options.
|
|
54
55
|
# @return The options.
|
|
55
|
-
#: [Config] (String
|
|
56
|
+
#: [Config] (String, Array[String], Class[Config], ?CacheOptions?, ?Optify::GetOptionsPreferences?) -> Config
|
|
56
57
|
def _get_options(key, feature_names, config_class, cache_options = nil, preferences = nil)
|
|
57
58
|
return get_options_with_cache(key, feature_names, config_class, cache_options, preferences) if cache_options
|
|
58
59
|
|
|
@@ -60,15 +61,14 @@ module Optify
|
|
|
60
61
|
Kernel.raise NotImplementedError,
|
|
61
62
|
"The provided config class must implement `from_hash` as a class method
|
|
62
63
|
in order to be converted.
|
|
63
|
-
Recommended: extend `Optify::
|
|
64
|
+
Recommended: extend `Optify::FromHashable`."
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
hash = JSON.parse(options_json)
|
|
67
|
+
hash = if preferences
|
|
68
|
+
get_options_hash_with_preferences(key, feature_names, preferences)
|
|
69
|
+
else
|
|
70
|
+
get_options_hash(key, feature_names)
|
|
71
|
+
end
|
|
72
72
|
config_class #: as untyped
|
|
73
73
|
.from_hash(hash)
|
|
74
74
|
end
|
|
@@ -79,8 +79,6 @@ module Optify
|
|
|
79
79
|
@features_with_metadata = nil #: Hash[String, OptionsMetadata]?
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
NOT_FOUND_IN_CACHE_SENTINEL = Object.new
|
|
83
|
-
|
|
84
82
|
#: [Config] (String key, Array[String] feature_names, Class[Config] config_class, Optify::CacheOptions _cache_options, ?Optify::GetOptionsPreferences? preferences) -> Config
|
|
85
83
|
def get_options_with_cache(key, feature_names, config_class, _cache_options, preferences = nil)
|
|
86
84
|
# Cache directly in Ruby instead of Rust because:
|
|
@@ -102,23 +100,22 @@ module Optify
|
|
|
102
100
|
# Features are filtered, so we don't need the constraints in the cache key.
|
|
103
101
|
are_configurable_strings_enabled = preferences&.are_configurable_strings_enabled? || false
|
|
104
102
|
cache_key = [key, feature_names, are_configurable_strings_enabled, config_class]
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
# Handle a cache miss.
|
|
103
|
+
@cache #: as !nil
|
|
104
|
+
.fetch(cache_key) do
|
|
105
|
+
# Handle a cache miss.
|
|
110
106
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
# We can avoid converting the features names because they're already converted from filtering above, if that was desired.
|
|
108
|
+
# We don't need the constraints because we filtered the features above.
|
|
109
|
+
# We already know there are no overrides because we checked above.
|
|
110
|
+
preferences = GetOptionsPreferences.new
|
|
111
|
+
preferences.skip_feature_name_conversion = true
|
|
112
|
+
preferences.enable_configurable_strings if are_configurable_strings_enabled
|
|
117
113
|
|
|
118
|
-
|
|
114
|
+
result = _get_options(key, feature_names, config_class, nil, preferences)
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
@cache #: as !nil
|
|
117
|
+
.[]= cache_key, result
|
|
118
|
+
end
|
|
122
119
|
end
|
|
123
120
|
end
|
|
124
121
|
end
|
data/rbi/optify.rbi
CHANGED
|
@@ -1,47 +1,26 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
1
|
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
# Tools for working with configurations declared in files.
|
|
5
5
|
module Optify
|
|
6
|
+
# DEPRECATED: Use `Optify::FromHashable` instead.
|
|
6
7
|
# A base class for classes from configuration files.
|
|
7
8
|
# Classes that derive from this can easily be used with `Optify::OptionsProvider.get_options`
|
|
8
9
|
# because they will have an implementation of `from_hash` that works recursively.
|
|
9
10
|
# This class is a work in progress with minimal error handling
|
|
10
11
|
# and doesn't handle certain cases such as nilable types yet.
|
|
11
12
|
# It may be moved to another gem in the future.
|
|
12
|
-
class BaseConfig
|
|
13
|
+
class BaseConfig < FromHashable
|
|
13
14
|
abstract!
|
|
14
|
-
|
|
15
|
-
# Create a new instance of the class from a hash.
|
|
16
|
-
#
|
|
17
|
-
# This is a class method that so that it can set members with private setters.
|
|
18
|
-
# @param hash The hash to create the instance from.
|
|
19
|
-
# @return The new instance.
|
|
20
|
-
sig { params(hash: T::Hash[T.untyped, T.untyped]).returns(T.attached_class) }
|
|
21
|
-
def self.from_hash(hash); end
|
|
22
|
-
|
|
23
|
-
# Convert this object to a Hash recursively.
|
|
24
|
-
# This is mostly the reverse operation of `from_hash`,
|
|
25
|
-
# as keys will be symbols
|
|
26
|
-
# and `from_hash` will convert strings to symbols if that's how the attribute is declared.
|
|
27
|
-
# @return The hash representation of this object.
|
|
28
|
-
sig { returns(T::Hash[Symbol, T.untyped]) }
|
|
29
|
-
def to_h; end
|
|
30
|
-
|
|
31
|
-
# Compare this object with another object for equality.
|
|
32
|
-
# @param other The object to compare.
|
|
33
|
-
# @return [Boolean] true if the objects are equal; otherwise, false.
|
|
34
|
-
sig { params(other: T.untyped).returns(T::Boolean) }
|
|
35
|
-
def ==(other); end
|
|
36
15
|
end
|
|
37
16
|
|
|
38
17
|
# Options for caching.
|
|
39
18
|
# Only enabling or disabling caching is supported for now.
|
|
40
|
-
class CacheOptions <
|
|
19
|
+
class CacheOptions < FromHashable
|
|
41
20
|
end
|
|
42
21
|
|
|
43
22
|
# Information about a feature.
|
|
44
|
-
class OptionsMetadata <
|
|
23
|
+
class OptionsMetadata < FromHashable
|
|
45
24
|
sig { returns(T.nilable(T::Array[String])) }
|
|
46
25
|
def aliases; end
|
|
47
26
|
|
|
@@ -163,6 +142,13 @@ module Optify
|
|
|
163
142
|
sig { returns(T::Hash[String, OptionsMetadata]) }
|
|
164
143
|
def features_with_metadata; end
|
|
165
144
|
|
|
145
|
+
# @return All of the keys and values for the the features.
|
|
146
|
+
sig do
|
|
147
|
+
params(feature_names: T::Array[String], preferences: GetOptionsPreferences)
|
|
148
|
+
.returns(T::Hash[String, T.untyped])
|
|
149
|
+
end
|
|
150
|
+
def get_all_options_hash(feature_names, preferences); end
|
|
151
|
+
|
|
166
152
|
# @return All of the keys and values for the the features.
|
|
167
153
|
sig do
|
|
168
154
|
params(feature_names: T::Array[String], preferences: GetOptionsPreferences)
|
|
@@ -214,7 +200,7 @@ module Optify
|
|
|
214
200
|
# @param feature_names The enabled feature names to use to build the options.
|
|
215
201
|
# @param config_class The class of the configuration to return.
|
|
216
202
|
# The class must implement `from_hash` as a class method to convert a hash to an instance of the class.
|
|
217
|
-
# It is recommended to use a class that extends `Optify::
|
|
203
|
+
# It is recommended to use a class that extends `Optify::FromHashable` because it implements `from_hash`.
|
|
218
204
|
# @param cache_options Set this if caching is desired. Only very simple caching is supported for now.
|
|
219
205
|
# @param preferences The preferences to use when getting options.
|
|
220
206
|
# @return The options.
|
|
@@ -231,6 +217,26 @@ module Optify
|
|
|
231
217
|
end
|
|
232
218
|
def get_options(key, feature_names, config_class, cache_options = nil, preferences = nil); end
|
|
233
219
|
|
|
220
|
+
# Fetches options based on the provided key and feature names.
|
|
221
|
+
#
|
|
222
|
+
# @param key [String] the key to fetch options for.
|
|
223
|
+
# @param feature_names [Array<String>] The enabled feature names to use to build the options.
|
|
224
|
+
# @return [Hash[String, T.untyped]] the options.
|
|
225
|
+
sig { params(key: String, feature_names: T::Array[String]).returns(T::Hash[String, T.untyped]) }
|
|
226
|
+
def get_options_hash(key, feature_names); end
|
|
227
|
+
|
|
228
|
+
# Fetches options based on the provided key and feature names.
|
|
229
|
+
#
|
|
230
|
+
# @param key [String] the key to fetch options for.
|
|
231
|
+
# @param feature_names [Array<String>] The enabled feature names to use to build the options.
|
|
232
|
+
# @param preferences [GetOptionsPreferences] The preferences to use when getting options.
|
|
233
|
+
# @return [String] the options in JSON.
|
|
234
|
+
sig do
|
|
235
|
+
params(key: String, feature_names: T::Array[String], preferences: GetOptionsPreferences)
|
|
236
|
+
.returns(T::Hash[String, T.untyped])
|
|
237
|
+
end
|
|
238
|
+
def get_options_hash_with_preferences(key, feature_names, preferences); end
|
|
239
|
+
|
|
234
240
|
# Fetches options in JSON format based on the provided key and feature names.
|
|
235
241
|
#
|
|
236
242
|
# @param key [String] the key to fetch options for.
|
data/sig/optify.rbs
CHANGED
|
@@ -2,40 +2,23 @@
|
|
|
2
2
|
module Optify
|
|
3
3
|
end
|
|
4
4
|
|
|
5
|
+
# DEPRECATED: Use `Optify::FromHashable` instead.
|
|
5
6
|
# A base class for classes from configuration files.
|
|
6
7
|
# Classes that derive from this can easily be used with `Optify::OptionsProvider.get_options`
|
|
7
8
|
# because they will have an implementation of `from_hash` that works recursively.
|
|
8
9
|
# This class is a work in progress with minimal error handling
|
|
9
10
|
# and doesn't handle certain cases such as nilable types yet.
|
|
10
11
|
# It may be moved to another gem in the future.
|
|
11
|
-
class Optify::BaseConfig
|
|
12
|
-
# Create a new instance of the class from a hash.
|
|
13
|
-
#
|
|
14
|
-
# This is a class method that so that it can set members with private setters.
|
|
15
|
-
# @param hash The hash to create the instance from.
|
|
16
|
-
# @return The new instance.
|
|
17
|
-
def self.from_hash: (::Hash[untyped, untyped] hash) -> instance
|
|
18
|
-
|
|
19
|
-
# Convert this object to a Hash recursively.
|
|
20
|
-
# This is mostly the reverse operation of `from_hash`,
|
|
21
|
-
# as keys will be symbols
|
|
22
|
-
# and `from_hash` will convert strings to symbols if that's how the attribute is declared.
|
|
23
|
-
# @return The hash representation of this object.
|
|
24
|
-
def to_h: () -> ::Hash[Symbol, untyped]
|
|
25
|
-
|
|
26
|
-
# Compare this object with another object for equality.
|
|
27
|
-
# @param other The object to compare.
|
|
28
|
-
# @return [Boolean] true if the objects are equal; otherwise, false.
|
|
29
|
-
def ==: (untyped other) -> bool
|
|
12
|
+
class Optify::BaseConfig < FromHashable
|
|
30
13
|
end
|
|
31
14
|
|
|
32
15
|
# Options for caching.
|
|
33
16
|
# Only enabling or disabling caching is supported for now.
|
|
34
|
-
class Optify::CacheOptions <
|
|
17
|
+
class Optify::CacheOptions < FromHashable
|
|
35
18
|
end
|
|
36
19
|
|
|
37
20
|
# Information about a feature.
|
|
38
|
-
class Optify::OptionsMetadata <
|
|
21
|
+
class Optify::OptionsMetadata < FromHashable
|
|
39
22
|
def aliases: () -> ::Array[String]?
|
|
40
23
|
|
|
41
24
|
# The canonical names of features that import this one.
|
|
@@ -127,6 +110,8 @@ class Optify::OptionsRegistry
|
|
|
127
110
|
# @return All of the keys and values for the the features.
|
|
128
111
|
def features_with_metadata: () -> ::Hash[String, OptionsMetadata]
|
|
129
112
|
|
|
113
|
+
def get_all_options_hash: (::Array[String] feature_names, GetOptionsPreferences preferences) -> ::Hash[String, untyped]
|
|
114
|
+
|
|
130
115
|
def get_all_options_json: (::Array[String] feature_names, GetOptionsPreferences preferences) -> String
|
|
131
116
|
end
|
|
132
117
|
|
|
@@ -156,6 +141,15 @@ module Optify::ProviderModule
|
|
|
156
141
|
|
|
157
142
|
def get_options: [Config] (String key, ::Array[String] feature_names, T::Class[Config] config_class, ?CacheOptions? cache_options, ?Optify::GetOptionsPreferences? preferences) -> Config
|
|
158
143
|
|
|
144
|
+
# Fetches options based on the provided key and feature names.
|
|
145
|
+
#
|
|
146
|
+
# @param key [String] the key to fetch options for.
|
|
147
|
+
# @param feature_names [Array<String>] The enabled feature names to use to build the options.
|
|
148
|
+
# @return [Hash[String, T.untyped]] the options.
|
|
149
|
+
def get_options_hash: (String key, ::Array[String] feature_names) -> ::Hash[String, untyped]
|
|
150
|
+
|
|
151
|
+
def get_options_hash_with_preferences: (String key, ::Array[String] feature_names, GetOptionsPreferences preferences) -> ::Hash[String, untyped]
|
|
152
|
+
|
|
159
153
|
# Fetches options in JSON format based on the provided key and feature names.
|
|
160
154
|
#
|
|
161
155
|
# @param key [String] the key to fetch options for.
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: optify-config
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.17.
|
|
4
|
+
version: 1.17.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin D. Harris
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rb_sys
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 0.9.117
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: optify-from_hash
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.2.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.2.0
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: sorbet-runtime
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,6 +85,34 @@ dependencies:
|
|
|
71
85
|
- - "~>"
|
|
72
86
|
- !ruby/object:Gem::Version
|
|
73
87
|
version: 4.0.0.dev.4
|
|
88
|
+
- !ruby/object:Gem::Dependency
|
|
89
|
+
name: rubocop
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: 1.76.1
|
|
95
|
+
type: :development
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 1.76.1
|
|
102
|
+
- !ruby/object:Gem::Dependency
|
|
103
|
+
name: rubocop-sorbet
|
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: 0.11.0
|
|
109
|
+
type: :development
|
|
110
|
+
prerelease: false
|
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - "~>"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: 0.11.0
|
|
74
116
|
- !ruby/object:Gem::Dependency
|
|
75
117
|
name: sorbet
|
|
76
118
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -160,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
160
202
|
- !ruby/object:Gem::Version
|
|
161
203
|
version: '0'
|
|
162
204
|
requirements: []
|
|
163
|
-
rubygems_version: 3.6.
|
|
205
|
+
rubygems_version: 3.6.7
|
|
164
206
|
specification_version: 4
|
|
165
207
|
summary: Configure your Ruby project using JSON and YAML files that can be combined
|
|
166
208
|
at runtime.
|