From ceb6eb43b17f69275aff143664e7057260a4351c Mon Sep 17 00:00:00 2001 From: Fabien Jakimowicz Date: Sun, 9 Aug 2009 15:24:06 +0200 Subject: [PATCH] add json format for errors validations in ActiveResource --- activeresource/lib/active_resource/base.rb | 6 ++- activeresource/lib/active_resource/validations.rb | 24 ++++++- activeresource/test/base_errors_test.rb | 77 +++++++++++++++------ 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 9db3588..99f7b83 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -169,7 +169,7 @@ module ActiveResource # # Active Resource supports validations on resources and will return errors if any of these validations fail # (e.g., "First name can not be blank" and so on). These types of errors are denoted in the response by - # a response code of 422 and an XML representation of the validation errors. The save operation will + # a response code of 422 and an XML or JSON representation of the validation errors. The save operation will # then fail (with a false return value) and the validation errors can be accessed on the resource in question. # # ryan = Person.find(1) @@ -178,10 +178,14 @@ module ActiveResource # # # When # # PUT http://api.people.com:3000/people/1.xml + # # or + # # PUT http://api.people.com:3000/people/1.json # # is requested with invalid values, the response is: # # # # Response (422): # # First cannot be empty + # # or + # # {"errors":["First cannot be empty"]} # # # # ryan.errors.invalid?(:first) # => true diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb index a2ba224..28f0c0d 100644 --- a/activeresource/lib/active_resource/validations.rb +++ b/activeresource/lib/active_resource/validations.rb @@ -7,11 +7,10 @@ module ActiveResource # Active Resource validation is reported to and from this object, which is used by Base#save # to determine whether the object in a valid state to be saved. See usage example in Validations. class Errors < ActiveModel::Errors - # Grabs errors from the XML response. - def from_xml(xml) + # Grabs errors from an array of messages (like ActiveRecord::Validations) + def from_array(messages) clear humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) } - messages = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue [] messages.each do |message| attr_message = humanized_attributes.keys.detect do |attr_name| if message[0, attr_name.size + 1] == "#{attr_name} " @@ -22,6 +21,18 @@ module ActiveResource self[:base] << message if attr_message.nil? end end + + # Grabs errors from the json response. + def from_json(json) + array = ActiveSupport::JSON.decode(json)['errors'] rescue [] + from_array array + end + + # Grabs errors from the XML response. + def from_xml(xml) + array = Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue [] + from_array array + end end # Module to support validation and errors with Active Resource objects. The module overrides @@ -56,7 +67,12 @@ module ActiveResource save_without_validation true rescue ResourceInvalid => error - errors.from_xml(error.response.body) + case error.response['Content-Type'] + when 'application/xml': + errors.from_xml(error.response.body) + when 'application/json': + errors.from_json(error.response.body) + end false end diff --git a/activeresource/test/base_errors_test.rb b/activeresource/test/base_errors_test.rb index 2881382..94abf0c 100644 --- a/activeresource/test/base_errors_test.rb +++ b/activeresource/test/base_errors_test.rb @@ -4,45 +4,80 @@ require "fixtures/person" class BaseErrorsTest < Test::Unit::TestCase def setup ActiveResource::HttpMock.respond_to do |mock| - mock.post "/people.xml", {}, "Age can't be blankName can't be blankName must start with a letterPerson quota full for today.", 422 + mock.post "/people.xml", {}, %q(Age can't be blankName can't be blankName must start with a letterPerson quota full for today.), 422, {'Content-Type' => 'application/xml'} + mock.post "/people.json", {}, %q({"errors":["Age can't be blank","Name can't be blank","Name must start with a letter","Person quota full for today."]}), 422, {'Content-Type' => 'application/json'} end - @person = Person.new(:name => '', :age => '') - assert_equal @person.save, false end def test_should_mark_as_invalid - assert !@person.valid? + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert !@person.valid? + end + end end def test_should_parse_xml_errors - assert_kind_of ActiveResource::Errors, @person.errors - assert_equal 4, @person.errors.size + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert_kind_of ActiveResource::Errors, @person.errors + assert_equal 4, @person.errors.size + end + end end def test_should_parse_errors_to_individual_attributes - assert @person.errors[:name].any? - assert_equal ["can't be blank"], @person.errors[:age] - assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name] - assert_equal ["Person quota full for today."], @person.errors[:base] + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + assert @person.errors[:name].any? + assert_equal ["can't be blank"], @person.errors[:age] + assert_equal ["can't be blank", "must start with a letter"], @person.errors[:name] + assert_equal ["Person quota full for today."], @person.errors[:base] + end + end end def test_should_iterate_over_errors - errors = [] - @person.errors.each { |attribute, message| errors << [attribute.to_s, message] } - assert errors.include?(["name", "can't be blank"]) + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + errors = [] + @person.errors.each { |attribute, message| errors << [attribute, message] } + assert errors.include?([:name, "can't be blank"]) + end + end end def test_should_iterate_over_full_errors - errors = [] - @person.errors.to_a.each { |message| errors << message } - assert errors.include?("Name can't be blank") + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + errors = [] + @person.errors.to_a.each { |message| errors << message } + assert errors.include?("Name can't be blank") + end + end end def test_should_format_full_errors - full = @person.errors.full_messages - assert full.include?("Age can't be blank") - assert full.include?("Name can't be blank") - assert full.include?("Name must start with a letter") - assert full.include?("Person quota full for today.") + [ :json, :xml ].each do |format| + invalid_user_using_format(format) do + full = @person.errors.full_messages + assert full.include?("Age can't be blank") + assert full.include?("Name can't be blank") + assert full.include?("Name must start with a letter") + assert full.include?("Person quota full for today.") + end + end + end + + private + def invalid_user_using_format(mime_type_reference) + previous_format = Person.format + Person.format = mime_type_reference + @person = Person.new(:name => '', :age => '') + assert_equal false, @person.save + + yield + ensure + Person.format = previous_format end end -- 1.6.2.1