Combining STI with a factory object

As an exercise, I am working on app for finding people to play RockBand with.

Users sign up as “Drummers”, “Guitarists” or “Singers”. Since the data is the same for all types, I decided to go with STI. These are my models:

class User < ActiveRecord::Base
  attr_accessible(
    :email,
    :name,
    :password,
    :password_confirmation,
    :type
  )
  has_secure_password
end

class Drummer < User
end

class GuitarPlayer < User
end

class Singer < User
end

My goal with this app is trying to apply design patterns, so I tried to create a UserFactory object.
It is called from the Users controller:

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    user = UserFactory.create_with_type(params[:user])
    session[:current_user] = user
    redirect_to dashboard_path
  end
end

and it looks like this:

class UserFactory
  def self.create_with_type(params)
    type = params.fetch(:type)
    type.constantize.create(params)
  end
end

My question is should I use a class method like I did or should I be instantiating a new UserFactory from the controller?

Changed the topic to “Rails”, sorry for the confusion.

Good question!

I usually make factory methods like yours class methods.

Thank you, also I’m not really sure how to test this.

I have the following feature test (from which the Factory + STI structure originates):

feature 'A visitor can signup' do
  scenario 'as a Drummer' do
    sign_up_as 'Drummer'

    user_should_see_greeting 'Welcome, example_drummer'
    expect(page).to have_css '[data-user-type="Drummer"]'
  end

 # other user types ommited here for brevity 

  def sign_up_as(user_type)
    visit sign_up_path
    
    fill_in 'Name', with: user_type.capitalize
    
    fill_in 'Email',
    with: "#{user_type.gsub(' ', '_').downcase}@example.com"
    
    fill_in 'PSN username',
    with: "example_#{user_type.gsub(' ', '_').downcase}"
    
    choose("user_type_#{user_type.gsub(' ', '').downcase}")
    click_button 'Sign up'
  end

  def user_should_see_greeting(greeting)
    expect(page).to have_css '.greeting', text: greeting
  end
end

And the following unit test:

describe UserFactory, '.create_with_type' do
  it 'creates a user of the type passed in' do
    params = attributes_for(:user, type: "Singer")

    user = UserFactory.create_with_type(params)

    expect(user.type).to eq "Singer"
  end
end

I’m not so sure about this unit test, any suggestions?

Looks fine to me.

Thanks @benorenstein Onward until the next bump in the road :slight_smile:

Good luck :smile:.