diff --git a/lib/her/model.rb b/lib/her/model.rb index 9d08d0dc..e4e38aa8 100644 --- a/lib/her/model.rb +++ b/lib/her/model.rb @@ -67,6 +67,9 @@ module Model # Configure ActiveModel callbacks extend ActiveModel::Callbacks define_model_callbacks :create, :update, :save, :find, :destroy, :initialize + + # Enable population of validation errors using well-formed @response_errors + # populate_validation_errors end end end diff --git a/lib/her/model/orm.rb b/lib/her/model/orm.rb index fa91b2df..9b3194f4 100644 --- a/lib/her/model/orm.rb +++ b/lib/her/model/orm.rb @@ -45,6 +45,8 @@ def save @metadata = parsed_data[:metadata] @response_errors = parsed_data[:errors] + populate_errors @response_errors if respond_to?(:populate_validation_errors) + return false if !response.success? || @response_errors.any? if self.changed_attributes.present? @previously_changed = self.changed_attributes.clone @@ -78,12 +80,31 @@ def destroy assign_attributes(self.class.parse(parsed_data[:data])) if parsed_data[:data].any? @metadata = parsed_data[:metadata] @response_errors = parsed_data[:errors] + + populate_errors @response_errors if respond_to?(:populate_validation_errors) + @destroyed = true end end self end + # Populate validation errors if @response_errors is formatted in this format + # + # { + # :email => ["can't be empty","is invalid"], + # :password => ["is too short"] + # } + # + def populate_errors(response_errors) + self.errors.clear + response_errors.each do |attribute,errors| + errors.each do |error| + self.errors.add(attribute,error) + end if errors.is_a? Array + end if response_errors.is_a? Hash + end + module ClassMethods # Create a new chainable scope # @@ -194,6 +215,12 @@ def build(attributes = {}) resource end + # Enables populate validation errors using @response_errors + def populate_validation_errors + class_attribute :populate_validation_errors + self.populate_validation_errors = true + end + private # @private def blank_relation diff --git a/spec/model/orm_spec.rb b/spec/model/orm_spec.rb index 5bf523a9..f04282e6 100644 --- a/spec/model/orm_spec.rb +++ b/spec/model/orm_spec.rb @@ -73,12 +73,18 @@ builder.adapter :test do |stub| stub.get("/users") { |env| [200, {}, { :data => [{ :id => 1, :name => "Tobias Fünke" }, { :id => 2, :name => "Lindsay Fünke" }], :metadata => { :total_pages => 10, :next_page => 2 }, :errors => ["Oh", "My", "God"] }.to_json] } stub.post("/users") { |env| [200, {}, { :data => { :name => "George Michael Bluth" }, :metadata => { :foo => "bar" }, :errors => ["Yes", "Sir"] }.to_json] } + stub.post("/user_with_validation_errors") { |env| [422, {}, { :data => { :name => "George Michael Bluth", :email => "george@micheal.com" }, :metadata => { :foo => "bar" }, :errors => {:name => ['already exists', 'too long'], :email => ['is invalid']} }.to_json] } end end spawn_model :User do uses_api api end + + spawn_model :UserWithValidationErrors do + uses_api api + populate_validation_errors + end end it "handles metadata on a collection" do @@ -100,6 +106,16 @@ @user = User.create(:name => "George Michael Bluth") @user.response_errors.should == ["Yes", "Sir"] end + + it "handles error data on a resource" do + @user = UserWithValidationErrors.create(:name => "George Michael Bluth", :email => "george@micheal.com") + @user.response_errors.should == {:name => ['already exists', 'too long'], :email => ['is invalid']} + + # ActiveSupport validations + @user.errors.messages.should == {:name => ['already exists', 'too long'], :email => ['is invalid']} + @user.errors.full_messages.should == ["Name already exists", "Name too long", "Email is invalid"] + end + end context "mapping data, metadata and error data in string keys to Ruby objects" do