0

Short explanation:

I seek architectural advice and help in implementing multiple Devise models in a single app.

More detailed explanation:

My application needs to perform the following behavior:

There are 3 types of users (Partner, Attendee, Speaker) which have some common fields and some unique ones (also, the fields might have different permissions, i.e. Attendee must have a username whereas the Speaker might have it but they don't have to necessarily fill this field in). And moreover, the different user models must have different associations with other tables in the db.

The users need to be able to log in through single log-in form, but the sign-up forms should be different.

So, my first thought was that I should divide the users by roles using Pundit or declarative_authorization or something, but the users don't really have different roles (i.e. permissions) in the app, they rather have different behavior, they might see different content and stuff, so I continued thinking.

My second though was implementing STI and after reading several articles about it, I tried to do that in code.

I generated a Devise User model by doing rails g devise User and after that ran rails g model Attendee and the same for other two users.

Then, I inherited my models from User:

class Attendee < User
end

My User migration looks like this:

create_table :users do |t|
  t.string :first_name
  t.string :last_name
  t.string :type

  # Devise stuff ...
  ..................

  t.timestamps null: false
end

And other migrations are like this:

create_table :attendees do |t|
  t.string :username
  t.string :company_name
  t.boolean :subscription

  t.timestamps null: false
end

Now I realize it was wrong to create separate tables. I had to put all the possible fields into the User table, is that correct? Because now when I try to create any new Attendee or Speaker or Partner in rails console, all of these three models have the exact same fields, those the User model has.

But if I add all the possible fields in the User model, how would I perform validations on field presence?


I've read quite a few articles and questions here on SO, but still can't really wrap my head around how to implement all that.

Anyway, is that a correct way to do what I need?

Could anybody explain me in a detailed way how I should implement this kind of behavior and functionality from start to finish, and how should I work with the models after having implemented them?

PS: here's the history of my migrations and the whole github repo


Update

Remembered another issue that stopped my from doing just role separation:

How should I sign up the different users with different sign-up forms? Different routes? I cannot make the user choose their role from the combobox.

Denis Yakovenko
  • 3,241
  • 6
  • 48
  • 82
  • This kind of sounds like code duplication to me... isn't it easier to create a role based system and allowing/disallowing certain fields depending on the role? (have a look at Rolify gem) – bo-oz Nov 19 '15 at 09:04
  • Oh sorry, you concluded that yourself already... you can check for the role in before_validation. I'll put some exemple code below – bo-oz Nov 19 '15 at 09:06
  • @bo-oz Yeah, that's what I thought as well. That's why I decided to ask for the piece of advice from more experienced developers. – Denis Yakovenko Nov 19 '15 at 09:06
  • See my snippet below for a quick explanation of conditional validation. – bo-oz Nov 19 '15 at 09:17

1 Answers1

1

You can create conditional validation rules based on the role, but the first place you need to address this is in the new/edit User form, only showing the allowed fields dynamically based on the role:

class User < ActiveRecord::Base
  validates :company, presence: true, if: :is_company?

  def is_company
   # check for the role
   self.role == 'company'
  end
end

UPDATE: You can pass an extra parameter to the same registration form and use that to differentiate the type of form you display. That's the nicest way. You can also create separate methods in the UserController -> def register_user, def register_company, def register_xxxx

bo-oz
  • 2,842
  • 2
  • 24
  • 44
  • Thanks for the usefult snippet! So, you suggest that I put all the possible fields in a single User table and just divide the roles? – Denis Yakovenko Nov 19 '15 at 09:19
  • Well that totally depends on the functionality you're gonna create afterwards. It sounds plausible that Company would require another model... Don't think programming wise, think functionality or object wise. A company is a different thing as a natural person. Maybe companies will require multiple users in the future... then you're screwed this way! – bo-oz Nov 19 '15 at 09:21
  • I thought it would be easier... Well, anyway, it seems that having just one `User` model will end up in a lot of role-based `if-else` mess in logic – Denis Yakovenko Nov 19 '15 at 09:24
  • You can have multiple Devise models if you wish... you just need to determine what's the best solution for your specific application. I cannot determine that with the information you are giving. Only that a Speaker sounds like a special type of User to me as does a Partner. If they are natural singular persons, than they fit perfectly in one User model. If you have all kinds of special features or other relationships each one of them has exclusively, then separating them is the best solution. – bo-oz Nov 19 '15 at 09:27