optify-config 1.15.2 → 1.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65334ccc886cf5a479eb7734f81d234bd088c85ed1e06a01d6693197076536d9
4
- data.tar.gz: daf480e5bfb9174c58ffcfaf07023a1f1f3fbf0370410e35271d7ee0e90987a9
3
+ metadata.gz: 61fd9d81e7b8808f374f13e8859da5f875ae2d57e6438ece70f905d92e370955
4
+ data.tar.gz: 4d3ccb53cea8d3c09519c7ec960735bb1fb49b03f2d6afbab2fc56f6d6ecb3e2
5
5
  SHA512:
6
- metadata.gz: 4fd19ad0474abdf115c39b24c595468b532cbb5fb97d85fc04da27612f070fe28daf5a367705319c81599ed22841e67cc94a35a416b2c31c387ab97134b3a3a1
7
- data.tar.gz: c72936edfbc178ac88427a529104a17f5466e37d4ce8fcf26f2ce3927f2d3c8e6fac3c22049a622b0d167b5b47ae6d898d5021303641296a0846cba62ed0258a
6
+ metadata.gz: f5f9f06f4f98e1ecf052a39f608a09900631eaf9cd8338a3a1e58468e84c7e59858b673e74b9b944c20f89c9e575d8eebbb3e142778e6a95a4e13e6926fd7b44
7
+ data.tar.gz: dd9f9b8a273198cfb52a7a017fd607a49111e21611019112dcaeac919eaa5bc7fefdc9119191910f05ec7dcd54c7f6aa5fa0ae856570fa8547301c5457d93027
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "optify_ruby"
3
- version = "0.17.0"
3
+ version = "0.18.0"
4
4
  edition = "2021"
5
5
 
6
6
  description = "optify bindings for Ruby"
@@ -21,6 +21,6 @@ crate-type = ["cdylib"]
21
21
 
22
22
  [dependencies]
23
23
  magnus = "0.8.0"
24
- optify = { path = "../../../../rust/optify", version = "0.18.0" }
24
+ optify = { path = "../../../../rust/optify", version = "0.19.0" }
25
25
  rb-sys = { version = "*", default-features = false, features = ["ruby-static"] }
26
26
  serde_json = "1.0.143"
@@ -2,69 +2,16 @@ use magnus::{function, method, prelude::*, wrap, Object, Ruby};
2
2
  use optify::builder::OptionsProviderBuilder;
3
3
  use optify::builder::OptionsRegistryBuilder;
4
4
  use optify::builder::OptionsWatcherBuilder;
5
- use optify::provider::GetOptionsPreferences;
6
5
  use optify::provider::OptionsProvider;
7
6
  use optify::provider::OptionsRegistry;
8
7
  use optify::provider::OptionsWatcher;
9
8
  use optify::schema::metadata::OptionsMetadata;
10
9
  use std::cell::RefCell;
11
10
 
12
- fn convert_preferences(
13
- preferences: &MutGetOptionsPreferences,
14
- ) -> std::cell::Ref<'_, GetOptionsPreferences> {
15
- preferences.0.borrow()
16
- }
17
-
18
- #[derive(Clone)]
19
- #[wrap(class = "Optify::GetOptionsPreferences")]
20
- struct MutGetOptionsPreferences(RefCell<GetOptionsPreferences>);
21
-
22
- impl MutGetOptionsPreferences {
23
- fn new() -> Self {
24
- Self(RefCell::new(GetOptionsPreferences {
25
- constraints: None,
26
- overrides_json: None,
27
- skip_feature_name_conversion: false,
28
- }))
29
- }
30
-
31
- // Constraints Section
32
- fn set_constraints_json(&self, constraints_json: Option<String>) {
33
- self.0
34
- .borrow_mut()
35
- .set_constraints_json(constraints_json.as_deref());
36
- }
37
-
38
- fn get_constraints_json(&self) -> Option<String> {
39
- self.0
40
- .borrow()
41
- .constraints
42
- .as_ref()
43
- .map(|c| serde_json::to_string(&c.constraints).unwrap())
44
- }
45
-
46
- // Overrides Section
47
- fn has_overrides(&self) -> bool {
48
- self.0.borrow().overrides_json.is_some()
49
- }
50
-
51
- fn set_overrides_json(&self, overrides: Option<String>) {
52
- self.0.borrow_mut().overrides_json = overrides;
53
- }
54
-
55
- fn get_overrides_json(&self) -> Option<String> {
56
- self.0.borrow().overrides_json.clone()
57
- }
58
-
59
- // Skip Feature Name Conversion Section
60
- fn set_skip_feature_name_conversion(&self, value: bool) {
61
- self.0.borrow_mut().skip_feature_name_conversion = value;
62
- }
11
+ use crate::preferences::convert_preferences;
12
+ use crate::preferences::MutGetOptionsPreferences;
63
13
 
64
- fn skip_feature_name_conversion(&self) -> bool {
65
- self.0.borrow().skip_feature_name_conversion
66
- }
67
- }
14
+ mod preferences;
68
15
 
69
16
  #[wrap(class = "Optify::OptionsProvider")]
70
17
  struct WrappedOptionsProvider(RefCell<OptionsProvider>);
@@ -180,6 +127,23 @@ impl WrappedOptionsProvider {
180
127
  serde_json::to_string(&self.0.borrow().get_features_with_metadata()).unwrap()
181
128
  }
182
129
 
130
+ fn get_filtered_features(
131
+ ruby: &Ruby,
132
+ rb_self: &Self,
133
+ feature_names: Vec<String>,
134
+ preferences: &MutGetOptionsPreferences,
135
+ ) -> Result<Vec<String>, magnus::Error> {
136
+ let preferences = &convert_preferences(preferences);
137
+ match rb_self
138
+ .0
139
+ .borrow()
140
+ .get_filtered_feature_names(&feature_names, Some(preferences))
141
+ {
142
+ Ok(features) => Ok(features),
143
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
144
+ }
145
+ }
146
+
183
147
  // Return a string because it wasn't clear how to return a type defined in Rust despite looking at docs and trying a few examples.
184
148
  fn get_options_json(
185
149
  ruby: &Ruby,
@@ -352,6 +316,23 @@ impl WrappedOptionsWatcher {
352
316
  serde_json::to_string(&self.0.borrow().get_features_with_metadata()).unwrap()
353
317
  }
354
318
 
319
+ fn get_filtered_features(
320
+ ruby: &Ruby,
321
+ rb_self: &Self,
322
+ feature_names: Vec<String>,
323
+ preferences: &MutGetOptionsPreferences,
324
+ ) -> Result<Vec<String>, magnus::Error> {
325
+ let preferences = &convert_preferences(preferences);
326
+ match rb_self
327
+ .0
328
+ .borrow()
329
+ .get_filtered_feature_names(&feature_names, Some(preferences))
330
+ {
331
+ Ok(features) => Ok(features),
332
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
333
+ }
334
+ }
335
+
355
336
  fn get_options_json(
356
337
  ruby: &Ruby,
357
338
  rb_self: &Self,
@@ -465,6 +446,10 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
465
446
  "get_canonical_feature_name",
466
447
  method!(WrappedOptionsProvider::get_canonical_feature_name, 1),
467
448
  )?;
449
+ provider_class.define_method(
450
+ "get_filtered_features",
451
+ method!(WrappedOptionsProvider::get_filtered_features, 2),
452
+ )?;
468
453
  provider_class.define_method(
469
454
  "get_options_json",
470
455
  method!(WrappedOptionsProvider::get_options_json, 2),
@@ -565,6 +550,10 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
565
550
  "get_canonical_feature_name",
566
551
  method!(WrappedOptionsWatcher::get_canonical_feature_name, 1),
567
552
  )?;
553
+ watcher_class.define_method(
554
+ "get_filtered_features",
555
+ method!(WrappedOptionsWatcher::get_filtered_features, 2),
556
+ )?;
568
557
  watcher_class.define_method(
569
558
  "get_options_json",
570
559
  method!(WrappedOptionsWatcher::get_options_json, 2),
@@ -0,0 +1,60 @@
1
+ use magnus::wrap;
2
+ use optify::provider::GetOptionsPreferences;
3
+ use std::cell::RefCell;
4
+
5
+ pub fn convert_preferences(
6
+ preferences: &MutGetOptionsPreferences,
7
+ ) -> std::cell::Ref<'_, GetOptionsPreferences> {
8
+ preferences.0.borrow()
9
+ }
10
+
11
+ #[derive(Clone)]
12
+ #[wrap(class = "Optify::GetOptionsPreferences")]
13
+ pub struct MutGetOptionsPreferences(RefCell<GetOptionsPreferences>);
14
+
15
+ impl MutGetOptionsPreferences {
16
+ pub fn new() -> Self {
17
+ Self(RefCell::new(GetOptionsPreferences {
18
+ constraints: None,
19
+ overrides_json: None,
20
+ skip_feature_name_conversion: false,
21
+ }))
22
+ }
23
+
24
+ // Constraints Section
25
+ pub fn set_constraints_json(&self, constraints_json: Option<String>) {
26
+ self.0
27
+ .borrow_mut()
28
+ .set_constraints_json(constraints_json.as_deref());
29
+ }
30
+
31
+ pub fn get_constraints_json(&self) -> Option<String> {
32
+ self.0
33
+ .borrow()
34
+ .constraints
35
+ .as_ref()
36
+ .map(|c| serde_json::to_string(&c.constraints).unwrap())
37
+ }
38
+
39
+ // Overrides Section
40
+ pub fn has_overrides(&self) -> bool {
41
+ self.0.borrow().overrides_json.is_some()
42
+ }
43
+
44
+ pub fn set_overrides_json(&self, overrides: Option<String>) {
45
+ self.0.borrow_mut().overrides_json = overrides;
46
+ }
47
+
48
+ pub fn get_overrides_json(&self) -> Option<String> {
49
+ self.0.borrow().overrides_json.clone()
50
+ }
51
+
52
+ // Skip Feature Name Conversion Section
53
+ pub fn set_skip_feature_name_conversion(&self, value: bool) {
54
+ self.0.borrow_mut().skip_feature_name_conversion = value;
55
+ }
56
+
57
+ pub fn skip_feature_name_conversion(&self) -> bool {
58
+ self.0.borrow().skip_feature_name_conversion
59
+ }
60
+ }
data/lib/optify.rb CHANGED
@@ -15,7 +15,7 @@ require_relative 'optify_ruby/watcher_implementation'
15
15
  # but that doesn't work when building for multiple versions of Ruby.
16
16
  # So we have to do this which is similar to something from 'https://github.com/matsadler/halton-rb/blob/main/lib/halton.rb'.
17
17
  begin
18
- ruby_version = T.must(RUBY_VERSION.match(/\d+\.\d+/))[0]
18
+ ruby_version = RUBY_VERSION.match(/\d+\.\d+/)&.[](0)
19
19
  require_relative "optify_ruby/#{ruby_version}/optify_ruby"
20
20
  rescue LoadError
21
21
  begin
@@ -6,8 +6,6 @@ require 'sorbet-runtime'
6
6
  module Optify
7
7
  # @!visibility private
8
8
  module ProviderModule
9
- extend T::Sig
10
-
11
9
  #: (Array[String] feature_names) -> Array[String]
12
10
  def get_canonical_feature_names(feature_names)
13
11
  # Try to optimize a typical case where there are just a few features.
@@ -40,7 +38,7 @@ module Optify
40
38
  end
41
39
  result.freeze
42
40
 
43
- @features_with_metadata = T.let(result, T.nilable(T::Hash[String, OptionsMetadata]))
41
+ @features_with_metadata = result
44
42
  result
45
43
  end
46
44
 
@@ -71,13 +69,14 @@ module Optify
71
69
  get_options_json(key, feature_names)
72
70
  end
73
71
  hash = JSON.parse(options_json)
74
- T.unsafe(config_class).from_hash(hash)
72
+ config_class #: as untyped
73
+ .from_hash(hash)
75
74
  end
76
75
 
77
76
  #: -> void
78
77
  def _init
79
- @cache = T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
80
- @features_with_metadata = T.let(nil, T.nilable(T::Hash[String, OptionsMetadata]))
78
+ @cache = {} #: Hash[untyped, untyped]?
79
+ @features_with_metadata = nil #: Hash[String, OptionsMetadata]?
81
80
  end
82
81
 
83
82
  NOT_FOUND_IN_CACHE_SENTINEL = Object.new
@@ -93,28 +92,31 @@ module Optify
93
92
  end
94
93
 
95
94
  init unless @cache
96
- feature_names = get_canonical_feature_names(feature_names) unless preferences&.skip_feature_name_conversion
97
95
 
98
- cache_key = [key, feature_names, preferences&.constraints_json, config_class]
99
- result = @cache&.fetch(cache_key, NOT_FOUND_IN_CACHE_SENTINEL)
96
+ if preferences.nil?
97
+ feature_names = get_filtered_features(feature_names, GetOptionsPreferences.new)
98
+ elsif !preferences.skip_feature_name_conversion || preferences.constraints_json
99
+ feature_names = get_filtered_features(feature_names, preferences)
100
+ end
101
+
102
+ # Features are filtered, so we don't need the constraints in the cache key.
103
+ cache_key = [key, feature_names, config_class]
104
+ result = @cache #: as !nil
105
+ .fetch(cache_key, NOT_FOUND_IN_CACHE_SENTINEL)
100
106
  return result unless result.equal?(NOT_FOUND_IN_CACHE_SENTINEL)
101
107
 
102
108
  # Handle a cache miss.
103
109
 
104
- # We can avoid converting the features names because they're already converted, if that was desired.
105
- if preferences.nil?
106
- preferences = GetOptionsPreferences.new
107
- preferences.skip_feature_name_conversion = true
108
- else
109
- # Indeed the copying of preferences could be wasteful, but this only happens on a cache miss
110
- # and when no custom preferences are provided.
111
- preferences = preferences.dup
112
- preferences.skip_feature_name_conversion = true
113
- end
110
+ # We can avoid converting the features names because they're already converted from filtering above, if that was desired.
111
+ # We don't need the constraints because we filtered the features above.
112
+ # We already know there are no overrides because we checked above.
113
+ preferences = GetOptionsPreferences.new
114
+ preferences.skip_feature_name_conversion = true
114
115
 
115
116
  result = get_options(key, feature_names, config_class, nil, preferences)
116
117
 
117
- T.must(@cache)[cache_key] = result
118
+ @cache #: as !nil
119
+ .[]= cache_key, result
118
120
  end
119
121
  end
120
122
  end
@@ -33,7 +33,7 @@ module Optify
33
33
  #: -> OptionsWatcher
34
34
  def init
35
35
  _init
36
- @cache_creation_time = T.let(Time.now, T.nilable(Time))
36
+ @cache_creation_time = Time.now #: Time?
37
37
  self
38
38
  end
39
39
 
data/rbi/optify.rbi CHANGED
@@ -183,6 +183,18 @@ module Optify
183
183
  sig { params(canonical_feature_name: String).returns(T.nilable(OptionsMetadata)) }
184
184
  def get_feature_metadata(canonical_feature_name); end
185
185
 
186
+ # Filters `feature_names` based on the preferences,
187
+ # such as the `preferences`'s constraints.
188
+ # Also converts the feature names to canonical feature names if `preferences.skip_feature_name_conversion` is `false`.
189
+ sig do
190
+ params(
191
+ feature_names: T::Array[String],
192
+ preferences: GetOptionsPreferences
193
+ )
194
+ .returns(T::Array[String])
195
+ end
196
+ def get_filtered_features(feature_names, preferences); end
197
+
186
198
  # Fetches options based on the provided key and feature names.
187
199
  #
188
200
  # @param key The key to fetch options for.
data/sig/optify.rbs CHANGED
@@ -142,6 +142,8 @@ module Optify::ProviderModule
142
142
  # @return The metadata for the feature.
143
143
  def get_feature_metadata: (String canonical_feature_name) -> OptionsMetadata?
144
144
 
145
+ def get_filtered_features: (::Array[String] feature_names, GetOptionsPreferences preferences) -> ::Array[String]
146
+
145
147
  def get_options: [Config] (String key, ::Array[String] feature_names, T::Class[Config] config_class, ?CacheOptions? cache_options, ?Optify::GetOptionsPreferences? preferences) -> Config
146
148
 
147
149
  # Fetches options in JSON format based on the provided key and feature names.
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.15.2
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin D. Harris
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-09-03 00:00:00.000000000 Z
10
+ date: 2025-09-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rb_sys
@@ -118,6 +118,7 @@ files:
118
118
  - ext/optify_ruby/Cargo.toml
119
119
  - ext/optify_ruby/extconf.rb
120
120
  - ext/optify_ruby/src/lib.rs
121
+ - ext/optify_ruby/src/preferences.rs
121
122
  - lib/optify.rb
122
123
  - lib/optify_ruby/base_config.rb
123
124
  - lib/optify_ruby/get_options_preferences.rb