Repository navigation
Conversation
numbata
left a comment
There was a problem hiding this comment.
Thanks for the contribution, @a5-stable!
A few things before this can merge:
-
CI is red.
spec/grape_entity/entity_spec.rb:1705still expects the result to be anOutputBuilder. It's a plainHashnow, so that line can go. The assertions after it already cover what matters. -
Please add a regression spec for the fix above. Use one
merge: trueexposure returning an array plus one plain exposure, and check thatserializable_hashis stable when called repeatedly. That keeps a future lazy wrapper from bringing the bug back. -
Please add a CHANGELOG entry.
serializable_hash/represent(..., serializable: true)now return a realHash/Array. That changesHash === result/Array === result(andcase/when) for downstream code, so users should hear about it.
I also left an inline comment on output.
| private | ||
|
|
||
| # If output_collection contains at least one element we have to represent the output as a collection | ||
| def output |
There was a problem hiding this comment.
output is public now, but it changes @output_collection every time it runs. If it's called twice when there's a merged array and a non-empty hash, @output_hash gets appended a second time.
As output = @output_collection doesn't copy the array. It makes a second name for the same array object, so everything done through output happens to @output_collection too. That's the same growing-result bug the old delegator had. One fix is to build the result without changing state:
def output
return @output_hash if @output_collection.empty?
[*@output_collection, *(@output_hash unless @output_hash.empty?)].flatten
endThe other option is to rename it to build!/finalize so it's clear it should only be called once. Renaming it would also stop the output local variable from shadowing the method.
There was a problem hiding this comment.
@numbata
Thanks!
I fixed output so it no longer mutates the builder's state, by @output_collection.dup:
def output
return @output_hash if @output_collection.empty?
result = @output_collection.dup
result << @output_hash unless @output_hash.empty?
result.flatten
end[*@output_collection, *(@output_hash unless @output_hash.empty?)].flatten would spread the hash into key/value pairs, so I could not use:
[*[%w[a b]], *{ name: 'John' }].flatten
# => ["a", "b", :name, "John"] (expected ["a", "b", { name: "John" }])I also added the spec about this behaviour.
0049cf9 to
7b69374
Compare
7b69374 to
3cfa434
Compare
Danger ReportErrors
|
|
@numbata
I also fixed the CI failure caused by the latest RuboCop (the new |
Summary
This change makes
serializable_hashreturn theHashorArraythatOutputBuilderbuilt, instead of theOutputBuilderitself.Since Rails 8.1, ActiveSupport serializes a plain
Hashin C, but anOutputBuilderis not aHash, so it falls back to the slower Ruby path.Also, because
OutputBuilderinherits fromSimpleDelegator, everyArrayreference in it goes throughconst_missing.OutputBuilderwas introduced in #204 only to build theHashorArray, so I think that there is no reason to use it for serialization.Benchmark results:
Benchmark