optify-config 1.17.4 → 1.17.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ddc8459ff6f74376a610782d4fc078ce91f197a971fd342505694396cf427657
4
- data.tar.gz: 6f77569afc22e2d209c8efd4f83d6dab2942b806f107a90e839eebb4f000a285
3
+ metadata.gz: bb889131ad05549592201854a95085f700c72a576d220c5f76f7169332d49cad
4
+ data.tar.gz: 9c7d21e4319ddc82e459ff85ddac506b3e740b28375b1dc7753ed6cc1af64858
5
5
  SHA512:
6
- metadata.gz: f62b8ffe0e38f0c3d8c69eba643da48e7de8cb790c8d5fa130064ad8b0713d309e6f8917cf886aa2ce481936243c94bf8a570c976d61549b31c5e558628ea502
7
- data.tar.gz: b3267d50c074c09ad428d29583daaa468f8fddebb2659cbddb5e44a02768cdef2ae76eace4ef1ebe1f9776b00459cac3529a4f55d6c459405e8402e516ae1946
6
+ metadata.gz: aee1ece9d60a043e3fb6c81c9152759dbacaca66e94e766eb3f5920fc4b13b3a2363b2fd97014a26c9ca75841a3233bdcaacc952099e74ff012dd7c9ec3b3589
7
+ data.tar.gz: 86b083e4b963b968a7162de2581a6b1ffd6878cf3be15f2b12adf1b56fe1f20b8ef4529ed5017e7982b235d86edc755eef2f47f46ab864463ee619ea028295c6
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
1
  # typed: false
2
+ # frozen_string_literal: true
3
3
 
4
4
  require 'mkmf'
5
5
  require 'rb_sys/mkmf'
@@ -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,5 +1,5 @@
1
- # frozen_string_literal: true
2
1
  # typed: strict
2
+ # frozen_string_literal: true
3
3
 
4
4
  # The implementation to use directly Ruby and with types declared.
5
5
  require_relative 'optify_ruby/get_options_preferences'
@@ -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
  module Optify
5
5
  # Preferences for `get_options`.
@@ -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 < BaseConfig
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 < BaseConfig
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::BaseConfig` because it implements `from_hash`.
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 key, Array[String] feature_names, Class[Config] config_class, ?CacheOptions? cache_options, ?Optify::GetOptionsPreferences? preferences) -> Config
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::BaseConfig`."
64
+ Recommended: extend `Optify::FromHashable`."
64
65
  end
65
66
 
66
- options_json = if preferences
67
- get_options_json_with_preferences(key, feature_names, preferences)
68
- else
69
- get_options_json(key, feature_names)
70
- end
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
- result = @cache #: as !nil
106
- .fetch(cache_key, NOT_FOUND_IN_CACHE_SENTINEL)
107
- return result unless result.equal?(NOT_FOUND_IN_CACHE_SENTINEL)
108
-
109
- # Handle a cache miss.
103
+ @cache #: as !nil
104
+ .fetch(cache_key) do
105
+ # Handle a cache miss.
110
106
 
111
- # We can avoid converting the features names because they're already converted from filtering above, if that was desired.
112
- # We don't need the constraints because we filtered the features above.
113
- # We already know there are no overrides because we checked above.
114
- preferences = GetOptionsPreferences.new
115
- preferences.skip_feature_name_conversion = true
116
- preferences.enable_configurable_strings if are_configurable_strings_enabled
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
- result = get_options(key, feature_names, config_class, nil, preferences)
114
+ result = get_options(key, feature_names, config_class, nil, preferences)
119
115
 
120
- @cache #: as !nil
121
- .[]= cache_key, result
116
+ @cache #: as !nil
117
+ .[]= cache_key, result
118
+ end
122
119
  end
123
120
  end
124
121
  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
 
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 < BaseConfig
19
+ class CacheOptions < FromHashable
41
20
  end
42
21
 
43
22
  # Information about a feature.
44
- class OptionsMetadata < BaseConfig
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::BaseConfig` because it implements `from_hash`.
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 < BaseConfig
17
+ class Optify::CacheOptions < FromHashable
35
18
  end
36
19
 
37
20
  # Information about a feature.
38
- class Optify::OptionsMetadata < BaseConfig
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
4
+ version: 1.17.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin D. Harris
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-10-26 00:00:00.000000000 Z
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.2
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.