H K
H K

Reputation: 1275

Rails - How to create the right kind of object from Single Table Inheritance?

I've set up a Rails project to use Single Table Inheritance because I have two types of Users - Senders and Receivers. Senders have a public_key property and Receivers have a phone_number property. They share name, email, and password properties through User.

My issue is that in the create function of the User controller, I'm trying to create one type or the other - either a Sender or Receiver - based on the value of a radio button on my signup form.

Here's the setup:

USER MODEL

class User < ActiveRecord::Base

  validates :name,  :presence => true, :length => { maximum: 50 }
  validates :password, presence: true

  self.inheritance_column = :user_type

  # We will need a way to know which types with subclass the User model
  def self.user_types
    %w(Sender Receiver)
  end
end

class Sender < User; end
class Receiver < User; end

SENDER MODEL

class Sender < User
  validates :public_key,  :presence => true
end

RECEIVER MODEL

class Receiver < User
  validates :phone_number,  :presence => true, :length => 10
end

USER CONTROLLER

class UsersController < ApplicationController
  #  before_action :set_user, only: [:show, :edit, :update, :destroy]
  before_action :set_user_type

  def index
    @users = user_type_class.all
  end

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    if(user_type.eql? "receiver")
      @user = Receiver.new(user_params)
    else
      @user = Sender.new(user_params)
  end
  ...
  private

    # allow views to access user_type
    def set_user_type
      @user_type = user_type
    end

    def user_type
      User.user_types.include?(params[:type]) ? params[:type] : "User"
    end

    def user_type_class
      user_type.constantize
    end

    # Use callbacks to share common setup or constraints between actions.
    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:name, :email, :password, :confirmation_password, :user_type)
    end
end

NEW USER FORM

<%= form_for(@user) do |f| %>
...
  <div class="user-type">
    <%= f.label :user_type, class: 'form-control' %>
    <%= radio_button_tag(:user_type, "sender")  %>
    <%= label_tag(:user_type_sender, "I am a Sender")  %>
    <%= radio_button_tag(:user_type, "receiver")  %>
    <%= label_tag(:user_type_receiver, "I am a Receiver")  %>
  </div>
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
 ...
<% end %>

In the create method of the User controller, I'm trying to make the correct type of User with this if statement:

if(user_type.eql? "receiver")
  @user = Receiver.new(user_params)
else
  @user = Sender.new(user_params)

based on the value recorded in the User form here:

<%= radio_button_tag(:user_type, "sender")  %>
<%= label_tag(:user_type_sender, "I am a Sender")  %>
<%= radio_button_tag(:user_type, "receiver")  %>
<%= label_tag(:user_type_receiver, "I am a Receiver")  %>

However, I always wind up with a Sender type object from the else statement. I'm thinking this means something is wrong with my if statement, if(user_type.eql? "receiver"); however, I cannot figure out what.

Thoughts?

Upvotes: 0

Views: 65

Answers (1)

San
San

Reputation: 1954

First of all, I think you do not need set_user_type method because you are not using the @user_type variable that its setting. Take out before_action from top of the controller as well.

Second, in user_type method you need to change params[:type] to params[:user_type] since that's the name of radio button tags in HTML.

def user_type
  User.user_types.include?(params[:user_type]) ? params[:user_type] : "User"
end

Third, you also need to capitalize the value attributes of radio button tags to "Sender" and "Receiver", because thats what you have in User.user_types array.

<%= radio_button_tag(:user_type, "Sender")  %>
<%= radio_button_tag(:user_type, "Receiver")  %>

Disclaimer: Not tested, but it should take care of your issues.

Upvotes: 1

Related Questions