optify-config 1.6.0 → 1.8.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: 36866b3812d07006be63012e27381c3cae692e3a4a4adc6c1c9d54dbfda32c26
4
- data.tar.gz: df8fa299cbd2bc4891011e60a847c1819ab83de05b668924576515ffab648773
3
+ metadata.gz: 987b7a6cc75f0b5ab66cf1862917aecc96c1efef0ca54a771e49f3c359694534
4
+ data.tar.gz: ea2b13f32633ca71b4d947ab71cd0dfa918891c2b796078dea1b79ad939fa2d8
5
5
  SHA512:
6
- metadata.gz: 57eec9c48450af7a1b0541d2e241215f4b5b7c374d0b8e5a39dcfee1608cab60f29f02483c9b7a8a4422bb70cbde863c659c5e7f01821e21f06bcb1cb7005cb3
7
- data.tar.gz: c8bec708ad806a205a3fe0d9dee33237e1c29b38c4c42ad8df21daf1d7c9910322e7b99abddeba512a3175b8b368b42db1e564be148e2731f078aec6e17e42fb
6
+ metadata.gz: f8ce4f21f5e025a0a2fef506ea348a7bd12d0d97196dd580cf87fb078cd4730a637b5796769228d20d10ee433e60f7c9e2782f5d5c3173063324306e249ad30e
7
+ data.tar.gz: e73782f6410d5e045df70ea7537f05504cea49a7bde3314a5902a9717cc6c2c9ded89ddd810eae372b8c67eab5e24f9bca1626b0f983f1186839ac5026b94e91
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "optify_ruby"
3
- version = "0.10.0"
3
+ version = "0.11.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.7.1"
24
- optify = { path = "../../../../rust/optify", version = "0.10.0" }
24
+ optify = { path = "../../../../rust/optify", version = "0.11.0" }
25
25
  rb-sys = { version = "*", default-features = false, features = ["ruby-static"] }
26
26
  serde_json = "1.0.140"
@@ -2,13 +2,13 @@ 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::convert_to_str_slice;
6
5
  use optify::provider::GetOptionsPreferences;
7
6
  use optify::provider::OptionsProvider;
8
7
  use optify::provider::OptionsRegistry;
9
8
  use optify::provider::OptionsWatcher;
10
9
  use optify::schema::metadata::OptionsMetadata;
11
10
  use std::cell::RefCell;
11
+ use std::path::Path;
12
12
 
13
13
  #[derive(Clone)]
14
14
  #[wrap(class = "Optify::GetOptionsPreferences")]
@@ -53,6 +53,23 @@ fn convert_metadata(metadata: &OptionsMetadata) -> String {
53
53
  }
54
54
 
55
55
  impl WrappedOptionsProvider {
56
+ fn build(ruby: &Ruby, directory: String) -> Result<WrappedOptionsProvider, magnus::Error> {
57
+ match OptionsProvider::build(Path::new(&directory)) {
58
+ Ok(provider) => Ok(WrappedOptionsProvider(RefCell::new(provider))),
59
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
60
+ }
61
+ }
62
+
63
+ fn build_from_directories(
64
+ ruby: &Ruby,
65
+ directories: Vec<String>,
66
+ ) -> Result<WrappedOptionsProvider, magnus::Error> {
67
+ match OptionsProvider::build_from_directories(&directories) {
68
+ Ok(provider) => Ok(WrappedOptionsProvider(RefCell::new(provider))),
69
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
70
+ }
71
+ }
72
+
56
73
  fn get_aliases(&self) -> Vec<String> {
57
74
  self.0.borrow().get_aliases()
58
75
  }
@@ -70,11 +87,10 @@ impl WrappedOptionsProvider {
70
87
  preferences: &MutGetOptionsPreferences,
71
88
  ) -> Result<String, magnus::Error> {
72
89
  let preferences = convert_preferences(preferences);
73
- let features = convert_to_str_slice!(feature_names);
74
90
  match rb_self
75
91
  .0
76
92
  .borrow()
77
- .get_all_options(&features, None, Some(&preferences))
93
+ .get_all_options(&feature_names, None, Some(&preferences))
78
94
  {
79
95
  Ok(options) => Ok(options.to_string()),
80
96
  Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
@@ -98,11 +114,10 @@ impl WrappedOptionsProvider {
98
114
  rb_self: &Self,
99
115
  feature_names: Vec<String>,
100
116
  ) -> Result<Vec<String>, magnus::Error> {
101
- let features = convert_to_str_slice!(feature_names);
102
117
  rb_self
103
118
  .0
104
119
  .borrow()
105
- .get_canonical_feature_names(&features)
120
+ .get_canonical_feature_names(&feature_names)
106
121
  .map_err(|e| magnus::Error::new(ruby.exception_arg_error(), e))
107
122
  }
108
123
 
@@ -129,11 +144,10 @@ impl WrappedOptionsProvider {
129
144
  key: String,
130
145
  feature_names: Vec<String>,
131
146
  ) -> Result<String, magnus::Error> {
132
- let features = convert_to_str_slice!(feature_names);
133
147
  match rb_self
134
148
  .0
135
149
  .borrow()
136
- .get_options_with_preferences(&key, &features, None, None)
150
+ .get_options_with_preferences(&key, &feature_names, None, None)
137
151
  {
138
152
  Ok(options) => Ok(options.to_string()),
139
153
  Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
@@ -148,10 +162,9 @@ impl WrappedOptionsProvider {
148
162
  preferences: &MutGetOptionsPreferences,
149
163
  ) -> Result<String, magnus::Error> {
150
164
  let preferences = convert_preferences(preferences);
151
- let features = convert_to_str_slice!(feature_names);
152
165
  match rb_self.0.borrow().get_options_with_preferences(
153
166
  &key,
154
- &features,
167
+ &feature_names,
155
168
  None,
156
169
  Some(&preferences),
157
170
  ) {
@@ -201,6 +214,23 @@ impl WrappedOptionsProviderBuilder {
201
214
  struct WrappedOptionsWatcher(RefCell<OptionsWatcher>);
202
215
 
203
216
  impl WrappedOptionsWatcher {
217
+ fn build(ruby: &Ruby, directory: String) -> Result<WrappedOptionsWatcher, magnus::Error> {
218
+ match OptionsWatcher::build(Path::new(&directory)) {
219
+ Ok(provider) => Ok(WrappedOptionsWatcher(RefCell::new(provider))),
220
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
221
+ }
222
+ }
223
+
224
+ fn build_from_directories(
225
+ ruby: &Ruby,
226
+ directories: Vec<String>,
227
+ ) -> Result<WrappedOptionsWatcher, magnus::Error> {
228
+ match OptionsWatcher::build_from_directories(&directories) {
229
+ Ok(provider) => Ok(WrappedOptionsWatcher(RefCell::new(provider))),
230
+ Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
231
+ }
232
+ }
233
+
204
234
  fn get_aliases(&self) -> Vec<String> {
205
235
  self.0.borrow().get_aliases()
206
236
  }
@@ -216,11 +246,10 @@ impl WrappedOptionsWatcher {
216
246
  preferences: &MutGetOptionsPreferences,
217
247
  ) -> Result<String, magnus::Error> {
218
248
  let preferences = convert_preferences(preferences);
219
- let features = convert_to_str_slice!(feature_names);
220
249
  match rb_self
221
250
  .0
222
251
  .borrow()
223
- .get_all_options(&features, None, Some(&preferences))
252
+ .get_all_options(&feature_names, None, Some(&preferences))
224
253
  {
225
254
  Ok(options) => Ok(options.to_string()),
226
255
  Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
@@ -244,11 +273,10 @@ impl WrappedOptionsWatcher {
244
273
  rb_self: &Self,
245
274
  feature_names: Vec<String>,
246
275
  ) -> Result<Vec<String>, magnus::Error> {
247
- let features = convert_to_str_slice!(feature_names);
248
276
  rb_self
249
277
  .0
250
278
  .borrow()
251
- .get_canonical_feature_names(&features)
279
+ .get_canonical_feature_names(&feature_names)
252
280
  .map_err(|e| magnus::Error::new(ruby.exception_arg_error(), e))
253
281
  }
254
282
 
@@ -273,11 +301,10 @@ impl WrappedOptionsWatcher {
273
301
  key: String,
274
302
  feature_names: Vec<String>,
275
303
  ) -> Result<String, magnus::Error> {
276
- let features = convert_to_str_slice!(feature_names);
277
304
  match rb_self
278
305
  .0
279
306
  .borrow()
280
- .get_options_with_preferences(&key, &features, None, None)
307
+ .get_options_with_preferences(&key, &feature_names, None, None)
281
308
  {
282
309
  Ok(options) => Ok(options.to_string()),
283
310
  Err(e) => Err(magnus::Error::new(ruby.exception_runtime_error(), e)),
@@ -292,10 +319,9 @@ impl WrappedOptionsWatcher {
292
319
  preferences: &MutGetOptionsPreferences,
293
320
  ) -> Result<String, magnus::Error> {
294
321
  let preferences = convert_preferences(preferences);
295
- let features = convert_to_str_slice!(feature_names);
296
322
  match rb_self.0.borrow().get_options_with_preferences(
297
323
  &key,
298
- &features,
324
+ &feature_names,
299
325
  None,
300
326
  Some(&preferences),
301
327
  ) {
@@ -353,6 +379,11 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
353
379
  builder_class.define_method("build", method!(WrappedOptionsProviderBuilder::build, 0))?;
354
380
 
355
381
  let provider_class = module.define_class("OptionsProvider", ruby.class_object())?;
382
+ provider_class.define_singleton_method("build", function!(WrappedOptionsProvider::build, 1))?;
383
+ provider_class.define_singleton_method(
384
+ "build_from_directories",
385
+ function!(WrappedOptionsProvider::build_from_directories, 1),
386
+ )?;
356
387
  provider_class.define_method("aliases", method!(WrappedOptionsProvider::get_aliases, 0))?;
357
388
  provider_class.define_method("features", method!(WrappedOptionsProvider::get_features, 0))?;
358
389
  provider_class.define_method(
@@ -428,6 +459,11 @@ fn init(ruby: &Ruby) -> Result<(), magnus::Error> {
428
459
  .define_method("build", method!(WrappedOptionsWatcherBuilder::build, 0))?;
429
460
 
430
461
  let watcher_class = module.define_class("OptionsWatcher", ruby.class_object())?;
462
+ watcher_class.define_singleton_method("build", function!(WrappedOptionsWatcher::build, 1))?;
463
+ watcher_class.define_singleton_method(
464
+ "build_from_directories",
465
+ function!(WrappedOptionsWatcher::build_from_directories, 1),
466
+ )?;
431
467
  watcher_class.define_method("aliases", method!(WrappedOptionsWatcher::get_aliases, 0))?;
432
468
  watcher_class.define_method("features", method!(WrappedOptionsWatcher::get_features, 0))?;
433
469
  watcher_class.define_method(
@@ -8,8 +8,7 @@ module Optify
8
8
  # A base class for classes from configuration files.
9
9
  # Classes that derive from this can easily be used with `Optify::OptionsProvider.get_options`
10
10
  # because they will have an implementation of `from_hash` that works recursively.
11
- # This class is a work in progress with minimal error handling
12
- # and doesn't handle certain cases such as nilable types yet.
11
+ # This class is a work in progress with minimal error handling.
13
12
  # It may be moved to another gem in the future.
14
13
  class BaseConfig
15
14
  extend T::Sig
@@ -26,7 +25,10 @@ module Optify
26
25
  instance = new
27
26
 
28
27
  hash.each do |key, value|
29
- sig_return_type = T::Utils.signature_for_method(instance_method(key)).return_type
28
+ sig = T::Utils.signature_for_method(instance_method(key))
29
+ raise "A Sorbet signature is required for `#{name}.#{key}`." if sig.nil?
30
+
31
+ sig_return_type = sig.return_type
30
32
  value = _convert_value(value, sig_return_type)
31
33
  instance.instance_variable_set("@#{key}", value)
32
34
  end
@@ -104,8 +106,47 @@ module Optify
104
106
  return true if other.equal?(self)
105
107
  return false unless other.is_a?(self.class)
106
108
 
107
- instance_variables.all? do |var|
108
- instance_variable_get(var) == other.instance_variable_get(var)
109
+ instance_variables.all? do |name|
110
+ instance_variable_get(name) == other.instance_variable_get(name)
111
+ end
112
+ end
113
+
114
+ # Convert this object to a Hash recursively.
115
+ # This is mostly the reverse operation of `from_hash`,
116
+ # as keys will be symbols
117
+ # and `from_hash` will convert strings to symbols if that's how the attribute is declared.
118
+ # @return [Hash] The hash representation of this object.
119
+ #: () -> Hash[Symbol, untyped]
120
+ def to_h
121
+ result = Hash.new(instance_variables.size)
122
+
123
+ instance_variables.each do |var_name|
124
+ # Remove the @ prefix to get the method name
125
+ method_name = var_name.to_s[1..] #: as !nil
126
+ value = instance_variable_get(var_name)
127
+ result[method_name.to_sym] = _convert_value_to_hash(value)
128
+ end
129
+
130
+ result
131
+ end
132
+
133
+ private
134
+
135
+ #: (untyped value) -> untyped
136
+ def _convert_value_to_hash(value)
137
+ case value
138
+ when Array
139
+ value.map { |v| _convert_value_to_hash(v) }
140
+ when Hash
141
+ value.transform_values { |v| _convert_value_to_hash(v) }
142
+ when nil
143
+ nil
144
+ else
145
+ if value.respond_to?(:to_h)
146
+ value.to_h
147
+ else
148
+ value
149
+ end
109
150
  end
110
151
  end
111
152
  end
data/rbi/optify.rbi CHANGED
@@ -76,6 +76,20 @@ module Optify
76
76
  class OptionsRegistry
77
77
  abstract!
78
78
 
79
+ class << self
80
+ # Build using just one directory.
81
+ # @param directory The directory to build the provider from.
82
+ # @return The instance.
83
+ sig { params(directory: String).returns(OptionsRegistry) }
84
+ def build(directory); end
85
+
86
+ # Build from multiple directories.
87
+ # @param directories The directories to build the provider from.
88
+ # @return The instance.
89
+ sig { params(directories: T::Array[String]).returns(OptionsRegistry) }
90
+ def build_from_directories(directories); end
91
+ end
92
+
79
93
  # @return All of the aliases.
80
94
  sig { returns(T::Array[String]) }
81
95
  def aliases; end
data/sig/optify.rbs CHANGED
@@ -60,6 +60,16 @@ end
60
60
 
61
61
  # A registry of features that provides configurations.
62
62
  class Optify::OptionsRegistry
63
+ # Build using just one directory.
64
+ # @param directory The directory to build the provider from.
65
+ # @return The instance.
66
+ def build: (String directory) -> OptionsRegistry
67
+
68
+ # Build from multiple directories.
69
+ # @param directories The directories to build the provider from.
70
+ # @return The instance.
71
+ def build_from_directories: (::Array[String] directories) -> OptionsRegistry
72
+
63
73
  # @return All of the aliases.
64
74
  def aliases: () -> ::Array[String]
65
75
 
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.6.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin D. Harris
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-11 00:00:00.000000000 Z
10
+ date: 2025-06-30 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rb_sys
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.5.12083
32
+ version: 0.5.12167
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.5.12083
39
+ version: 0.5.12167
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rake-compiler
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -57,42 +57,42 @@ dependencies:
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 3.9.3
60
+ version: 4.0.0.dev.4
61
61
  type: :development
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 3.9.3
67
+ version: 4.0.0.dev.4
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: sorbet
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.5.12083
74
+ version: 0.5.12167
75
75
  type: :development
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: 0.5.12083
81
+ version: 0.5.12167
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: tapioca
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: 0.16.11
88
+ version: 0.17.2
89
89
  type: :development
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: 0.16.11
95
+ version: 0.17.2
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: test-unit
98
98
  requirement: !ruby/object:Gem::Requirement