-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enum from_value when Flags #3518
Conversation
Bump, it's been a week. It's literally just one button press to merge. |
Before this PR on non flag enums, if a number that does not belongs to the enum is given as an argument, then After this PR if the enum acts as flag the user can build enum with values outside the declared constants. Maybe a bitwise & should be applied before returning the enum value. Also, if this is 0, and there is no 0 as enum value, I would expect |
Hi, Thanks for the PR but it's not quite right. The idea is that @[Flags]
enum ColorFlags
Red = 1
Green = 2
Blue = 4
end Then: ColorFlags.from_value?(1) # => Red
ColorFlags.from_value?(3) # => Red, Green
ColorFlags.from_value?(5) # => Red, Blue
ColorFlags.from_value?(7) # => Red, Green, Blue
# But...
ColorFlags.from_value?(8) # => nil
ColorFlags.from_value?(9) # => nil
... So this is not the same as |
@asterite I have been looking into this more, and have been working on updating my implementation, I have a question though, in your above example from_value?(9), shouldn't that return Red since the bitwise & of 9 and 1 == 1? |
Maybe use some sort of base-of-x math? |
@bmulvihill 9 is not a valid value, because yes, 1 is in it, but it's not an exact combination of all the enum values. One way to implement this, I guess, is to subtract each of the enum values from the given value, if the value is greater than the enum value. In the end the value should end up being zero. I don't know if there's another way to do this. |
Another approach would be to use bitwise operators to valid the value. Given @[Flags]
enum ColorFlags
Red = 1
Green = 2
Blue = 4
end if the |
@bcardiff I implemented a mask to determine valid values, the behavior now seems to be what is expected. |
arr = values.select {|v| (v.to_i & value) == v.to_i && v.to_i != 0 } | ||
return arr.reduce { |types, e| types | e } unless arr.empty? | ||
mask = {% for member, i in @type.constants %}\ | ||
{% if i != 0 %} | {% end %}\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK \
at line endings are considered bad practice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but in macros the alternative is to join everything in a single line and it is less readable IMO.
Since the macro has test coverage I think we are fine here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think is fine to merge this. All cases are handled now.
@asterite?
mask = {% for member, i in @type.constants %}\ | ||
{% if i != 0 %} | {% end %}\ | ||
{{@type}}::{{member}}.value{% end %} | ||
return if (mask & value != value) || (value == 0 && !values.map(&.to_i).includes?(0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a map
here, so this creates a temporary array. The implementation of this method should be efficient, without allocating memory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think values.any? { |val| val.to_i == 0 }
is right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ill update it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, using none?
and dropping the not is probably nicer.
GTG? |
@bmulvihill Thank you! ❤️ |
Fixes #3448
from_value will return the same as new for Enum with Flags