-
Type:
Task
-
Resolution: Unresolved
-
Priority:
Unknown
-
None
-
Affects Version/s: None
-
Component/s: None
-
None
-
None
-
Ruby Drivers
-
None
-
None
-
None
-
None
-
None
-
None
BSON::Document has various type confusion problems with its parent class Hash, I've identified many things that should be fixed.
Let's first look at the existing code to understand how BSON::Document is intended to function:
- BSON::Document extends Hash, and is intended to be a "Hash whose keys cannot be symbols"; they can be String or Numeric, possibly other things BSON allows.
- To acheive this, it overrides methods such as `merge!` to call the `convert_key` and `convert_value` methods at the correct places.
- However, the implementation is not complete, and there are lots of gaps where the underlying Hash / symbol keys are exposed.
Here is what I've found:
1. #to_h does not convert nested hashes. (See RUBY-3663)
2. When we call many of the Hash methods which it overrides (especially non-bang methods), they return Hash instances rather than BSON::Document. (Examples: .try_convert, #select, #reject, #invert)
3. Some of the bang methods `#transform_keys!` and `#transform_values!` can be used to transform BSON::Documents into a disallowed, state, e.g. with Symbol keys or Hash values (nested values should always be BSON document).
4. `#deep_symbolize_keys` (non-bang) plainly doesn't work; it should return a Hash with deeply symbolized keys, so all nested BSON::Documents need to be converted to Hash. In a similar vein, #symbolize_keys should return a deep-converted Hash, but with only the first-level Hash's keys symbolized. (It does not make sense to mix Hash with nested BSON::Document, you're gonna have a bad time.)
5. For methods that select keys, such as #key?, #delete, #slice etc. should always consistently be calling #convert_key on the key args. Today this is done inconsistently, for example, #key?, #delete, #dig all convert the keys, but #values_at, #fetch_values, #store, #without, #assoc do not.
6. Some needed aliases are missing. Also #value? is incorrectly aliases as #value (without question mark) which doesn't exist on Hash (coding mistake)
7. Minor point, but in the code, the method definiton of #dig is wrapped in instance_methods.include?(:dig). This is no longer needed, since dig was added in Ruby 2.5 and the minimum Ruby version of BSON Ruby is 2.6.
8. `Hash(document)` does not convert actually convert to a non-document hash. (See RUBY-3662)