Skip to content

Commit 954cecc

Browse files
committed
Implement Errors#from_array in terms of #from_hash
This commit changes the `ActiveResource::Errors#from_array` method to be implemented in terms of its complementary `#from_hash` method. Rather than modifying the `Errors` instance itself (like managing attribute and `:base` assignment, clearing, etc), `#from_array` will instead transform the Array instance into a Hash keyed by known attributes. Once the Hash is constructed, the method hands it off to the `#from_hash` method. The goal of this change is to reduce the duplicated burden of responsibility. Along with the implementation change, this commit also includes explicit coverage of the `Errors#from_xml` and `Errors#from_json` methods. They're part of the gem's public API (since they're documented and not marked `# :nodoc:`), but aren't explicitly tested elsewhere in the suite. There are two XML-focused tests. The first covers the case where the payload has multiple `<error>` elements: ```ruby Hash.from_xml "<errors><error>Name can't be blank</error><error>Email can't be blank</error></errors>" # => {"errors"=>{"error"=>["Name can't be blank", "Email can't be blank"]}} ``` The call to `Hash.from_xml` transforms each `<error>` element into an item in the Array. The second test covers the case where the payload has a single payload has a single `<error>` element: ```ruby Hash.from_xml "<errors><error>Name can't be blank</error></errors>" # => {"errors"=>{"error"=>"Name can't be blank"}} ``` In this case, The call to `Hash.from_xml` transforms the `<error>` element into a single String value. Since the `Errors#from_xml` method is part of the public API, and since its implementation invokes `Errors#from_array`, dedicating explicit test coverage for both scenarios can help prevent regressions or breaks in backwards-compatibility.
1 parent bd154d2 commit 954cecc

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

lib/active_resource/validations.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@ class Errors < ActiveModel::Errors
1414
# The second parameter directs the errors cache to be cleared (default)
1515
# or not (by passing true).
1616
def from_array(messages, save_cache = false)
17-
clear unless save_cache
17+
errors = Hash.new { |hash, attr_name| hash[attr_name] = [] }
1818
humanized_attributes = Hash[@base.known_attributes.map { |attr_name| [ attr_name.humanize, attr_name ] }]
19-
messages.each do |message|
19+
messages.each_with_object(errors) do |message, hash|
2020
attr_message = humanized_attributes.keys.sort_by { |a| -a.length }.detect do |attr_name|
2121
if message[0, attr_name.size + 1] == "#{attr_name} "
22-
add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
22+
hash[humanized_attributes[attr_name]] << message[(attr_name.size + 1)..-1]
2323
end
2424
end
25-
add(:base, message) if attr_message.nil?
25+
hash["base"] << message if attr_message.nil?
2626
end
27+
28+
from_hash errors, save_cache
2729
end
2830

2931
# Grabs errors from a hash of attribute => array of errors elements

test/cases/validations_test.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,61 @@ def new_project(opts = {})
7676
Project.new(VALID_PROJECT_HASH.merge(opts))
7777
end
7878
end
79+
80+
class ErrorsTest < ActiveSupport::TestCase
81+
def test_from_xml_with_multiple_errors
82+
errors = Project.new.errors
83+
84+
errors.from_xml %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Name can't be blank</error><error>Email can't be blank</error></errors>)
85+
86+
assert_equal [ "can't be blank" ], errors[:name]
87+
assert_equal [ "can't be blank" ], errors[:email]
88+
end
89+
90+
def test_from_xml_with_one_error
91+
errors = Project.new.errors
92+
93+
errors.from_xml %q(<?xml version="1.0" encoding="UTF-8"?><errors><error>Name can't be blank</error></errors>)
94+
95+
assert_equal [ "can't be blank" ], errors[:name]
96+
end
97+
98+
def test_from_json
99+
errors = Project.new.errors
100+
101+
errors.from_json %q({"errors":{"name":["can't be blank"],"email":["can't be blank"]}})
102+
103+
assert_equal [ "can't be blank" ], errors[:name]
104+
assert_equal [ "can't be blank" ], errors[:email]
105+
end
106+
107+
def test_from_hash
108+
errors = Project.new.errors
109+
110+
errors.from_hash(
111+
"base" => [ "has an error" ],
112+
"unknown" => [ "is invalid" ],
113+
"name" => [ "can't be blank" ],
114+
"email" => [ "can't be blank" ]
115+
)
116+
117+
assert_equal [ "has an error", "Unknown is invalid" ], errors[:base]
118+
assert_equal [ "can't be blank" ], errors[:name]
119+
assert_equal [ "can't be blank" ], errors[:email]
120+
end
121+
122+
def test_from_array
123+
errors = Project.new.errors
124+
125+
errors.from_array [
126+
"Unknown is invalid",
127+
"Base has an error",
128+
"Name can't be blank",
129+
"Email can't be blank"
130+
]
131+
132+
assert_equal [ "Unknown is invalid", "Base has an error" ], errors[:base]
133+
assert_equal [ "can't be blank" ], errors[:name]
134+
assert_equal [ "can't be blank" ], errors[:email]
135+
end
136+
end

0 commit comments

Comments
 (0)