tl;dr
You can accomplish this with standard Ruby tools using the raw URL. I have the following helper method in my ApplicationController to support this:
# Support for flat array params, `URI#query` can return `nil`
def raw_params = CGI.parse(URI(request.url).query || "")
Major Caveat
I must support this as it's a requirement of many third party APIs. However, unless you have a compelling business need, I'd echo the strong recommendations to not deviate from conventions. It requires more work down the line and copious comments so I don't forget why I'm doing goofy looking things when I revisit the code.
Examples
Faraday requires a connection specifically for this:
Faraday.new(request: {params_encoder: Faraday::FlatParamsEncoder})
WebMock needs to be configured to support this:
WebMock::Config.instance.query_values_notation = :flat_array
Stubs cannot use the more descriptive syntax and work off a raw string:
# This is my working test because…
stub_request(:get, "https://domain.tld/resource?param=foo¶m=bar")
.to_return(…)
# this won't work as `WebMock will add `[]`s
stub_request(:get, "https://domain.tld/resource")
.with(query: {param: ["foo", "bar"]})
.to_return(…)
And finally, query params need to be manually constructed differently:
# I need to do
URI.encode_www_form({param: ["foo", "bar"]}) #=> "param=foo¶m=bar"
# vs. pure Rails
{param: ["foo", "bar"]}.to_query #=> param%5B%5D=foo¶m%5B%5D=bar
None of this is truly abhorrent and I still sleep well at night but conventions make things so much easier to work with and vastly decrease your cognitive load when coding.