I agree that this is unexpected behavior at first, but it actually makes some good sense.
Consider, for example, what you would expect this to do:
output = {'foo' => 'bar'}.to_json
render :json => output
Even though the to_json
is kinda redundant, you expect the result to be {foo: "bar"}
. However, note that the result of {'foo' => 'bar'}.to_json
is actually a string. So, the above code block is equivalent to:
render :json => '{foo: "bar"}'
If render
were to JSON-encode strings passed to :json
, you would get "{foo: \"bar\"}"
, which is definitely not expected behavior.
So here’s the deal: render
checks to see if the :json
argument is a string. If so, it assumes that it’s a JSON string and you already ran to_json
, and passes the string along. If not, it runs to_json
on the object.
I think the documentation should probably clarify that, but there you have it. Though it’s not exactly intuitive at first glance, I would be surprised if it worked any other way.