This project is archived and is in readonly mode.
Correct output from ActiveModel::Errors to_json
Reported by Samuel Kadolph | June 17th, 2010 @ 06:02 PM | in 3.0.2
ActiveModel::Errors#to_xml correctly outputs valid XML that can be used to display all errors. ActiveModel::Errors#to_json does not output valid JSON because it does not have a custom implementation of to_json to deal with ActiveModel::Errors#each which flattens the attribute errors array.
irb(main):001:0> u = User.create
=> #<User id: nil, username: nil>
irb(main):002:0> u.errors
=> #<OrderedHash {:username=>["is too short (...snip...)", "can't be blank"]}>
irb(main):003:0> puts u.errors.to_xml
<?xml version="1.0" encoding="UTF-8"?>
<errors>
<error>Username is too short (minimum is 3 characters)</error>
<error>Username can't be blank</error>
</errors>
=> nil
irb(main):004:0> puts u.errors.to_json
{"username":"is too short (minimum is 3 characters)","username":"can't be blank"}
=> nil
This JSON is invalid and when parsed would only give one error
message per attribute.
ActiveModel::Errors#to_json should return the same messages as
to_xml.
["Username is too short (minimum is 3 characters)","Username can't be blank"]
Comments and changes to this ticket
-
Samuel Kadolph June 17th, 2010 @ 06:11 PM
The solution is to override the ActiveModel::Errors#as_json method to give the full_messages array to be converted to JSON. Patch is attached.
-
Samuel Kadolph June 17th, 2010 @ 06:45 PM
Complete patch is attached. (Sorry, new to the whole patching rails thing.)
-
José Valim June 22nd, 2010 @ 04:39 PM
- Milestone cleared.
- Assigned user set to José Valim
The patch looks great, but it need tests to ensure we won't have a regression later.
-
Neeraj Singh June 23rd, 2010 @ 10:07 PM
Trying to create a test for this one but running into stack level too deep issue.
Hopefully I'll have a test patch soon. :-)
-
Samuel Kadolph June 23rd, 2010 @ 10:11 PM
I've been looking into writing a test case for this but I have to get the tests for rails going first which hasn't been easy on Windows and our only other Unix server with ruby 1.8.7 is for production and I won't be installing the oodles of gems that rails dev requires.
That aside, I did notice there is no test case for ActiveModel::Errors#to_xml. So it would be beneficial to write a case for that as well.
-
José Valim June 25th, 2010 @ 08:41 AM
Neeraj, I think it's expected that you run into StackLevelTooDeep before the patch is applied. :) It means your test is correct!
-
Neeraj Singh June 25th, 2010 @ 03:48 PM
- Tag changed from active model, active_model, errors, json, to_json to active model, active_model, errors, json, rails3, to_json
- State changed from new to open
There are couple of things going on which is making me pause and think.
-
in the test if I do topic.errors.to_json then it blows up saying to_json is not defined which does not make sense since active_model requires active_support which in turn requires json. So all objects should have to_json. I don't fully understand how autoload works so I need to learn that.
-
if I explicitly say require active_support/json to bring to_json then I get "stack level too deep" even with the fix in.
I am looking into it. expect some delays :-)
-
José Valim June 25th, 2010 @ 04:05 PM
JSON is autoloaded, but the to_json method is only added after you load it. So you need to explicitly require the json file (in tests).
About the stack level too deep error, could you please paste the strack trace here (not necessarily the whole backtrace ;))?
-
Neeraj Singh June 25th, 2010 @ 04:15 PM
test requires 'cases/helper'. helper in turn requires active_model which in turn requires active_support.
JSON is eagerly autoloaded in active_support. json.rb in turn requires encoding. So I fail to understand why to_json will be missing. Sorry for being so thick.
eager_autoload do autoload :JSON end require 'active_support/json/decoding' require 'active_support/json/encoding'
-
Neeraj Singh June 25th, 2010 @ 04:19 PM
I added following line to test/cases/validations/presence_validation_test.rb
require "active_support/json/encoding" # added last three tests test 'accepts array arguments' do Topic.validates_presence_of %w(title content) t = Topic.new assert t.invalid? assert_equal ["can't be blank"], t.errors[:title] assert_equal ["can't be blank"], t.errors[:content] assert_match %r{<error>Title can't be blank</error>}, t.errors.to_xml assert_match %r{<error>Content can't be blank</error>}, t.errors.to_xml assert_nothing_raised { t.errors.to_json } end
This is the exception I am getting in its entirety
$ ruby -Itest test/cases/validations/presence_validation_test.rb -n /test_accepts_array_arguments/ Loaded suite test/cases/validations/presence_validation_test Started F Finished in 0.048406 seconds. 1) Failure: test_accepts_array_arguments(PresenceValidationTest) [test/cases/validations/presence_validation_test.rb:44:in `test_accepts_array_arguments' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:67:in `__send__' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:67:in `run' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/callbacks.rb:408:in `_run_setup_callbacks' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:65:in `run']: Exception raised: Class: <SystemStackError> Message: <"stack level too deep"> ---Backtrace--- test/cases/validations/presence_validation_test.rb:44:in `to_json' test/cases/validations/presence_validation_test.rb:44:in `test_accepts_array_arguments' test/cases/validations/presence_validation_test.rb:44:in `test_accepts_array_arguments' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:67:in `__send__' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:67:in `run' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/callbacks.rb:408:in `_run_setup_callbacks' /Users/nsingh/dev/working/rails_tickets/r_4890/vendor/bundler/gems/rails-16a5e918a06649ffac24fd5873b875daf66212ad-master/activesupport/lib/active_support/testing/setup_and_teardown.rb:65:in `run' --------------- 1 tests, 6 assertions, 1 failures, 0 errors
-
José Valim June 26th, 2010 @ 10:51 AM
- Importance changed from to Low
Ok, I went deep down this problem and it turned out to be the JSON gem overwriting the to_json implementation for all core classes so Rails implementation was never used. The gem implementation does not work with hash inheritance, making ActiveSupport::OrderedHash fail when serialized to_json (and consequently ActiveModel::Errors). Patch is coming soon.
-
Repository June 26th, 2010 @ 11:10 AM
- State changed from open to resolved
(from [7bd85a8fc2d216a5e2b1d0380df572f782a54d1c]) Work around the fact the JSON gem was overwriting to_json implementation for all Ruby core classes.
This is required because the JSON gem is incompatible with Rails behavior and was not allowing ActiveModel::Errors to be serialized.
So we need to ensure Rails implementation is the one triggered. [#4890 state:resolved]
http://github.com/rails/rails/commit/7bd85a8fc2d216a5e2b1d0380df572...
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 4890 Correct output from ActiveModel::Errors to_json This is required because the JSON gem is incompatible wit...