Introduce Boolean and Enum type support in LowType#34
Introduce Boolean and Enum type support in LowType#34purvi1239 wants to merge 1 commit intolow-rb:mainfrom
Conversation
|
Thank you for this PR. There are a few people interested in this issue via GSoC and it will take a while to sort through the solutions and apply the most suitable one. I have a few proposals via GSoC, including a strong proposal, so I will have to get all of the solutions in my head at once somehow :) Your contribution is appreciated and I will read it and provide feedback |
| raise Low::ConfigError, "Enum.symbols expects only symbols, got #{non_symbols.map(&:inspect).join(', ')}" | ||
| end | ||
|
|
||
| private_class_method |
There was a problem hiding this comment.
The examples I see use private_class_method :build_enum_type the line after the class method is defined, can we use it this way?
| require_relative '../../lib/expressions/type_expression' | ||
| require_relative '../../lib/syntax/union_types' | ||
|
|
||
| RSpec.describe 'Low::Types::Boolean and Low::Types::Enum' do |
There was a problem hiding this comment.
Should be 2 separate files, unit tests should test individual units
|
Hi @maedi,
Good catch, thanks for pointing that out.
Yes, using private_class_method :build_enum_type after the method
definition is intentional and follows standard Ruby practice. Since Ruby
doesn’t support declaring a class method as private inline, the method is
first defined and then its visibility is set using private_class_method.
This ensures that build_enum_type remains an internal helper and isn’t
exposed as part of the public API.
Happy to adjust if you’d prefer the alternative class << self style, but
this pattern is quite common across Ruby codebases.
Let me know your thoughts.
…On Sun, May 3, 2026 at 6:23 AM maedi ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In lib/types/enum.rb
<#34 (comment)>:
> + # type = Low::Types::Enum[:draft, :published]
+ # type.match?(value: :draft) # => true
+ def self.[](*values, strict: false)
+ definition = Definition.new(values: values, strict: strict)
+ build_enum_type(definition)
+ end
+
+ # Convenience helper for symbol-only enums.
+ def self.symbols(*symbols, strict: false)
+ non_symbols = symbols.reject { |v| v.is_a?(Symbol) }
+ return self[*symbols, strict: strict] if non_symbols.empty?
+
+ raise Low::ConfigError, "Enum.symbols expects only symbols, got #{non_symbols.map(&:inspect).join(', ')}"
+ end
+
+ private_class_method
The examples I see use private_class_method :build_enum_type the line
after the class method is defined, can we use it this way?
—
Reply to this email directly, view it on GitHub
<#34 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BJJYZ52C47WAHYEMSSXTBO34Y2KARAVCNFSM6AAAAACW5VLQDGVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHM2DEMJVGY4TOMBQGM>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
Thanks for the suggestion — that makes sense.
I agree that separating the specs would improve clarity and better align
with the idea of testing units in isolation. we should split this into two
separate spec files: one for Low::Types::Boolean and another for
Low::Types::Enum.
Appreciate the feedback!
Thank You
…On Sun, May 3, 2026 at 6:25 AM maedi ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In spec/units/boolean_enum_spec.rb
<#34 (comment)>:
> @@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require_relative '../../lib/types/complex_types'
+require_relative '../../lib/types/error_types'
+require_relative '../../lib/expressions/type_expression'
+require_relative '../../lib/syntax/union_types'
+
+RSpec.describe 'Low::Types::Boolean and Low::Types::Enum' do
Should be 2 separate files, unit tests should test individual units
—
Reply to this email directly, view it on GitHub
<#34 (review)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BJJYZ562UERAFYR5442UECT4Y2KJBAVCNFSM6AAAAACW5VLQDGVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHM2DEMJVGY4TQMZSGM>
.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
| "Expected true/false, got #{value.inspect}" | ||
| end | ||
|
|
||
| def self.inspect |
There was a problem hiding this comment.
Is the normal output of .inspect to be avoided? Can we just use that?
|
|
||
| private | ||
|
|
||
| def normalize_values(values) |
There was a problem hiding this comment.
If normalize_values ultimately ends up in Set.new() then they will be made unique automatically by Set
| # type = Low::Types::Enum[:draft, :published] | ||
| # type.match?(value: :draft) # => true | ||
| def self.[](*values, strict: false) | ||
| definition = Definition.new(values: values, strict: strict) |
There was a problem hiding this comment.
Definition.new(values:, strict:) is equivalent
| end | ||
|
|
||
| # Convenience helper for symbol-only enums. | ||
| def self.symbols(*symbols, strict: false) |
There was a problem hiding this comment.
Is this only called from a spec? If so it shouldn't be in the implementation as it's just expecting data be a certain way. Move the method to spec_helper.rb or create a more literal/simpler expect in the spec just checking for the values. The concept of Enums be restricted to a certain type is not something the implementation needs... we don't have a feature like this to my memory. So it must be just a test/spec side thing
| private_class_method | ||
|
|
||
| def self.build_enum_type(definition) | ||
| Class.new do |
There was a problem hiding this comment.
This class feels like we don't need it. It's a wrapper around the Definition class... when we could just instantiate that class instead instead of this one
| def matchable_type?(expression:) | ||
| expression.is_a?(Class) && | ||
| expression.respond_to?(:match?) && | ||
| expression.ancestors.include?(Low::Types::MatchableType) |
There was a problem hiding this comment.
Let's keep the first version simple. We can remove include Low::Types::MatchableType everywhere and just rely on expression.respond_to?(:match?) for now. We check the behaviour of the object rather than the type of the object (duck typing)
There was a problem hiding this comment.
And a better type based way of checking this without adding an extra module would be to check if the class has Low::Types as one of its ancestors (works for both Enum and Definition. This would work in a perfect world where other people in other namespaces aren't adding their own complex types... which they're currently not. So match? is probably enough for now and could be added by anyone anywhere in another complex type in another namespace
|
Please see this discussion for a few good pointers on this issue: |
Overview
This PR introduces support for Boolean and Enum types in LowType, improving expressiveness and allowing more precise runtime validation.
What’s Added
Boolean Type
trueandfalseTrueClass | FalseClassEnum Type
Enum[val1, val2, ...]type constructorEnum::Definitionwithmatch?validationIntegration Details
TypeExpressionto support non-Class types viamatch?TypeQueryto recognize Enum definitions as valid types|)Example Usage