Bob
Bob

Reputation: 1625

How can I return custom errors to my form in Rails?

I have a simple form on a website where a user enters a mailing address.

I have a service which can validate this address and return various responses, either Success, Suspect, or Invalid, as well as return the full and most complete zip code for that address. If the response is "Success", then I will save it to the db. If the response is "Suspect", then I will update the zip field and ask them to confirm. If the response is "Invalid" then I will return an error message asking them to contact us in person.

I'm trying to set up my rails create action such that it makes a call to my service (for example http:/addresssValidator.com) and I want to inform the user if they have a valid address or not, and update the zip with the suggested zip code.

Looking for address validation in rails however, seems to only give me APIs for using the built in error and validation system in rails and not how to return my own custom results to the form.

How can I do this?

Below is my code:

def create
  @valid = validate_address
    @address = Address.new(address_params)
  if @valid
    if @address.save
      redirect_to "/survey/success"
    else
      p @address.errors
      respond_to do |format|
        format.json {render json: @address.errors}
        format.html {render "/survey/failure"}

      end

    end
  else
    ##//Display errors
  end

  end 

def validate_address

@api_key = "my_api_key"
HTTParty.post("http://addressValidator.com",
              {
                  :body => [
                      "StreetAddress" =>  params[:address][:address_line_2] + " " + params[:address][:address_line_1],
                      "City" => params[:address][:city],
                      "PostalCode" => params[:address][:zip],
                      "State" => params[:address][:state],
                      "CountryCode" => params[:address][:country],
                      "Locale" => "en" ].to_json,
                  :APIKey => @api_key ,
                  :headers => { 'Content-Type' => 'application/json', 'Accept' => 'application/json'}
              })


return false;
##//return actual results from validator
  end

Upvotes: 5

Views: 4049

Answers (2)

max
max

Reputation: 102036

Models do the work, forms are stupid

In Rails you add errors to your models. To add an error you would do something like this:

if @address.country == "Sealand"
  @address.errors[:country] << ["we don't ship there"] 
end

In Rails forms are just are just simple form-builders bound to a model which create HTML. They don't actually have a role in validation besides displaying errors.

Custom validations

Rails lets you create custom validator classes and HTTParty is made to extend classes.

Lets mosh them together:

class AddressValidator < ActiveModel::Validator
  include HTTParty
  
  base_uri 'addressValidator.com'
  format :json
  
  def validate(address)
    response = self.post('/',
      # Note that I am just guessing here.
      body: {
        "StreetAddress" => address.street_address,
        "City" => address.city,
        "PostalCode" => address.zip,
        "State" =>  address.state,
        "CountryCode" => address.country
      }
    )
    if response.success?
      # bind the errors to the model
      unless response["StreetAddress"] == "valid"
        record.errors[:street_address] << response["StreetAddress"]
      end
    else
      e = response.response
      logger.warn "Failed to remotely validate Address. #{ e.message }"
      address.errors[:base] << "Failed to remotely validate!"
    end
  end
end 

Of course you will need to adapt this to the actual response from the API and your model. This is just a starting point demonstrating the coarse strokes.

So how would you use it?

def Address < ActiveRecord::Base
  validates_with AddressValidator

  # ...
end

Upvotes: 4

Ziv Galili
Ziv Galili

Reputation: 1445

If you want to add a custom error to a specific field:

@address.errors[:FIELD_NAME] << "Custom Error Message"

that way the error is added to the instance that you have, and will be shown in the form itself as the other errors appear.

if you just want to display an error, you can add it to the flash[:error] like this:

flash[:error] = "Invalid address"

if you want the error to move to the next method (like if you will use redirect after it), or:

flash.now[:error] = "Invalid address" 

if you want the error to be available only in current action. as @illusionist said in comment.

and in the html code, check if it exists, and if so, print it:

<% if flash[:error] %>
  <%= flash[:error] %>
<% end %>

in both ways, you can add the error from @valid.

If you want to store the new zip, add @address.zip = @valid.zip. That way it will show it in the field in the form.

Upvotes: 5

Related Questions