Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Displaying validation errors in client application forms #50

Open
artem-mindrov opened this Issue · 0 comments

1 participant

@artem-mindrov

Hi Darcy,
Thanks for the handy gem. I've got one question I couldn't find an easy solution for, maybe you can help me out with this.
I have two separate applications (one for regular users, another administration) with a single user base, the admin app acting as a web service for the user app (the API client). I need to display validation errors on login/registration forms in the user app. Since the user app is not backed by a DB, things become a little unwieldy on the client side since I don't get any of the ActiveModel convenience stuff out of the box when using RP clients instead of ActiveRecord models. Here's what I had to do to get the errors displayed properly.

I defined base classes for clients and the hashes they encapsulate (they are later used to trick form builder into binding input fields to attributes). The encapsulated 'model' has to have a model_name method and an ActiveModel error hash, otherwise simple_form_for will die with exceptions I've spent about 3 hours debugging :(

module Api
  class Base < RocketPants::Client
    version 1
    base_uri Rails.application.config.api.base_uri

    attr_accessor :auth_token

    def base_request_options
      session[:user] ? { headers: { 'X-AUTH' => session[:user][:auth_token] } } : {}
    end
  end

  class ModelBase < APISmith::Smash
    property :id
    property :errors

    def self.model_name
      ActiveModel::Name.new(self)
    end

    def to_key
      id
    end

    def populate_errors_from(error_hash)
      self.errors = ActiveModel::Errors.new(self)

      error_hash.each do |attr, msgs|
        msgs.each { |msg| self.errors.add(attr, msg) }
      end
    end
  end
end

Now, a client for the User model:

class Api::UserClient < Api::Base
  class User < Api::ModelBase
    property :first_name
    property :last_name
    property :email
    property :password
    property :password_confirmation
    property :provider_id
  end

  attr_reader :user

  def initialize(*args)
    @user = User.new(*args)
    super(*args)
  end

  def find(id)
    get "/user", extra_query: { id: id }
  end

  def save
    post "/users", extra_query: { user: @user }
  end
end

The corresponding controller on the client application side is below. It rescues the invalid record exception raised by the client base code, populates the error hash and re-renders the action with the form.

class UsersController < ApplicationController
  skip_before_filter :authenticate, only: [:new, :create]

  def create
    @user_client = Api::UserClient.new(params[:user])

    respond_to do |format|
      begin
        @user_client.save
      rescue RocketPants::InvalidResource => e
        format.html do
          @user = @user_client.user
          @user.populate_errors_from(e.errors)
          render action: :new
        end
      else
        format.html { redirect_to root_path, notice: t("signed_up_but_not_approved") }
      end
    end
  end
end

This works, but feels way too convoluted for a simple scenario like this. I feel there should be an easier way to do this.
I guess this post is a little too long, but anyway I'd be grateful if you could read through this and point me in the right direction here. I hope I've explained everything clear enough.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.