Question

I've got three classes Admin, Client, Agent which all inherit from User < ActiveRecord::Base. The system is designed using STI, hence all classes share the same users table.

I am interested in keeping the CRUD functionality pretty much the same, hence for now I am using a single UsersController.

When implementing the Update functionality I'm faced with a doubt. Here's my edit form:

#Edit Form
<%= form_for(@user,{url:user_path(@user),method: :put}) do |f| %>
    <%= render 'edit_fields',f:f %>
    <%= f.submit "Save", class: "btn btn-large btn-primary"%>
<% end %>
#UsersController
def edit
    @user=User.find(params[:id])
end
def update
    binding.pry
   #if @user.update_attributes(params[:user])   #<---BEFORE
   #WORKAROUND BELOW
   if @user.update_attributes(params[@user.type.downcase.to_sym])
       flash[:success]="User was updated successfully."
       redirect_to user_path(@user)
   else
        flash[:danger]="User could not be updated."
        render 'new'
   end
end

My "problem" is that params is dependent on the @user.type of the @user instance. Therefore sometimes there's a params[:client], other times a params[:admin] or params[:agent]. Hence the line if @user.update_attributes(params[:user]) does not always work.

The workaround I implemented works fine, but I was wondering whether there's a more DRY or elegant way to approach these issues, i.e. sharing CRUD between different STI-ed classes.

Was it helpful?

Solution

There is indeed a much more elegant solution. Just change your form_for declaration and add the as option, like this:

<%= form_for(@user, as: :user, url: user_path(@user), method: :put) do |f| %>

That way in your controller your parameters will be scoped under the user key instead of the model's class.

OTHER TIPS

In your controller, check for the User type in a before_filter as below. I've used this for similar STI Controllers and works great for me.

before_filter :get_user_type

private

def get_user_type
   @klass = params[:type].blank? ? User : params[:type].constantize
end

And then for example you could call a show method as :

def show
  @user = @klass.find params[:id]
  #render
end

Using @klass across your CRUD actions should simplify your Controller.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top