In Rails 3 application I have 2 logical entities - User and Company.
I'd like to have 2 different forms for sign up(for users and for companies). Also, will be great to have one login form for both of them.
What I have now - configured Devise+Cancan for User model with two roles(user, company), so I have now "/users/sign_in" and "/users/sign_up".
I'd like to have following urls in my application:
/login
/users/signup
/companies/signup
One other question is how to organize relationship between User and Company, should the company inherited from User or I shoud use aggregation - User has_one Company ? I prefer second variant and plan to user user.company with cancan user role = "company".
Please help me with it. Thanks.
You can have multiple models in devise. You can add a company as well.
rails generate devise company
This will get you the url you mentioned.
Regarding the relationship between User and Company It's common to use:
class User < ActiveRecord::Base
belongs_to :company
end
class Company < ActiveRecord::Base
has_many :users
end
You have to add a company_id column to the User model in a migration to make this happen. There is no inheritance then. Users and companies are treated completely separate. But you are able to access user.company and company.users.
Related
Rails newbie: I currently have a basic app where Customers(users) have many points (customer model and points model). And I want an admin user (new model) to have the ability to add points to the customer.
-The customer enters their phone number (#index route).
-If the customer is not found, they will be brought to a signup page (#new/#create route).
-If customer is found in the database, their profile will show (#show route).
Now on this page, I want to be able to have an admin passcode, which once entered, gives access to adding points. I also want to keep track of which admin user, gives which customer points on a different page.
How would the schema look like with the admin user? How would I give it access certain access to features like adding points. (I assume I'm going to have to create a helper method for checking if admin is logged in, and keep track of that somehow, maybe with sessions?)
class Customer < ActiveRecord::Base
has_many :points
end
class Point < ActiveRecord::Base
belongs_to :customer
end
class Admin < ActiveRecord::Base
#??? (my best guess is has_many :points, has_many :customers)
end
www.loyalty-app.herokuapp.com
I think the best way is to not have a special admin class and just make admins a type of customer (or user). Then use a gem like cancan or access granted (I prefer access granted) to handle what the different types of users can do.
Using devise, what is the best way to have multiple models or roles?
Here are the models or roles I need in my app:
-Author: can upload content to the site
-Customer: pays a monthly fee to access the content
A user can be both an Author and a Customer. I think they can share the same login form because they will both log in with their email address.
I have tried using CanCan and rolify, but couldn't figure out how to add different roles during registration. When a user registers as an Author, he should be given the "author" role and when a user registers by paying the monthly fee, he should be given the "customer" role. I don't want to use a checkbox for the roles either.
There is a nav link called "Authors" that will allow authors to register and then there is a link for users to register by going through the payment and billing form. Based on which link is clicked, should determine which role is given. Should I pass in a role parameter in the url during registration like "user/sign_up/index?role=customer"? If so, how do I get devise to use this in the registration process?
Using devise, CanCan, and rolify, how can I solve this? I thought it might be better to have a User class and then have Customer and Author extend from User, but from reading many similar questions on StackOverflow it seems rolify is easier.
I know what I am trying to do is very basic, I just haven't figured it out yet. I have been trying to find a solution for this all day.
Update:
Danny's answer below pushed me in the right direction and I have managed to get the roles added properly during registration by passing in a parameter in the URL "users/sign_up?role=customer" or "users/sign_up?role=author". I don't know if this is the best way, but that's what I have so far.
One question I have though is how will my has_many relations work now? Here is what I have:
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :plan
has_many :files
end
has_one :plan is for the customer's subscription plan
and has_many :files
is for the author who can upload many files that the customer can
download
The problem is that I would also like to keep track of the user's file downloads so I would like to put has_many :downloads or probably has_many :files, :through => :downloads
Does it matter if I put these in the User model knowing that an author who is not a customer is not allowed to download files and that a customer who is not an author cannot upload files?
From your question, I understand you've already "dived" into rolify and cancan, so I'll focus on assigning roles.
You typically create different roles in your seeds.rb file, with something like
[:admin, :author, :customer].each do |role|
Role.find_or_create_by_name({ name: role }, without_protection: true)
end
You assign the :admin role immediately when you create yourself as administrator, also in the seeds.rb file, using
user.add_role :admin
For your other roles, you assign them "when it's appropriate", exactly as you describe. When a user clicks the Authors link, and proceeds, this triggers some action in some controller. It is there that you assign this role to that user, by using the same
user.add_role :author
You can also assign roles, connected to certain objects only. E.g. an author is author for only the documents he creates himself. In that case you don't want to assign a "general" author role, but you will assign it like
user.add_role :author, document
in the controller's create action, together with saving the created document. Alternative for this kind of assignment is to do it in a callback from your model.
More questions? Just ask!
I'm trying to figure out how to set up the relationship/association for a Document model for my rails project. I have a User model and Users can friend each another (friendship model).
Now I want users to be able to invite (give access to) CERTAIN friends to modify and edit these documents similar to Google Docs.
This is my current approach to this problem:
Create a new relationship model called "group", essentially a subset of friends. Document belongs to a user and document belongs to a group. Users can then invite their friends into these group relationships and documents can be accessed/modified through these groups.
Therefore, User has many groups and group belongs to many users.
My question:
Is there a better approach to this problem?
You might consider a "Membership" join table. So:
Group >- Membership -< User
...since you requirement is some users will have abilities others wont (editing). There are all sorts of options from there, for example STI which would give you:
class Membership < ActiveRecord::Base
...general membership functions
end
class Editor < Membership
...editor specific code...
end
class Reviewer < Membership
...reviewer specific code...
end
And a controller class something like
class MembershipController
def create invitation
if invitation.for_editor?
# assuming invitation has one group, and group_memberships method that returns group.memberships
invitation.group_memberships.create! user: current_user
else
invitation.group_memberships.create! user: current_user
end
end
end
As an example, depending on your opinion of STI.
https://github.com/scambra/devise_invitable
might have more ideas.
I am developing a site where doctors can manage their patients, appointments, etc.
A doctor can have multiple assistants which help them with these activities, and an assistant can belong to multiple doctors.
Both doctors and assistants should be able to sign in from the same login form but for obvious reasons they have different permissions and associations depending on their role, for example patients are associated to the doctors, not to the assistant.
How you suggest would be the best way to approach this? I am contemplating CanCan for the authorization part, and maybe STI for a User model which Doctor and Assistant can inherit from for the authentication part, but can STI handle the HABTM between these 2, plus the other associations each model might have?
Thanks a lot in advance
This should do the trick.
Just make sure you use 'type' to set which the user is. You can then use things like:
current_user.is_doctor?
To determine access to areas.
module User
def is_doctor?
true if self.type == Doctor
end
def is_assistant?
true if self.type == Assistant
end
end
module Doctor < User
has_and_belongs_to_many :assistants, :through => "assistant_assignments"
end
module Assistant < User
has_and_belongs_to_many :doctors, :through => "assistant_assignments"
end
Shout if you need more info.
I'm designing an application that has two [three including administrators] user types: Buyers and Sellers. When a user signs up, it's assumed that they're signing up to simply purchase something [a Buyer account]. However, if they wish to become a Seller, there should be an option for them to post their items for sale, [become a Seller]. This becomes important, as users can switch back and forth between account types.
Of course, a Seller can buy items as well.
The problem
The problem I'm facing is that Sellers have profile pages where buyers can go to view their items for sale, whereas Buyers do not. However, both user types have a My Account page that they can use to update their information.
Possible design choices
Single table inheritence
class User < ActiveRecord::Base
has_one :profile
end
class Seller < User
has_many :sale_items
end
class Buyer < User
# nothing here.. I guess this is the "default" user type
end
I thought about this approach because then I could clearly separate the user types. For example, the show page for each user is clearly separated. However, this could lead to repeated code in each controller, and introduce a problem when switching between user types.
Just use declarative_authorization or CanCan to add functionality to the base user type
class User < ActiveRecord::Base
has_one :profile
has_many :sale_items # only for Sellers
# if the user is a seller, allow them to add sale_items
end
I thought of this approach because a Seller is basically a Buyer with additional functionality, such as posting items for sale. This could lead to a lot of view logic, though. For example if #user.role == "seller" render _some_seller_partial. I also don't like the idea of checking for a hard coded string in the view. Well, I guess I could do if #user.seller?
Other design choices?
I'm really interested in hearing how other people would model this application. I've been thinking about this for a couple days now.
I would use the second option, but with declarative_authorization instead of cancan, and I'd use the role_model Gem to store the role in the user model.
class User < ActiveRecord::Base
has_one :profile
has_many :sale_items # only for Sellers
# if the user is a seller, allow them to add sale_items
# t.integer :roles_mask , :default => 0 # for role_model
end
declarative_authorization is a bit more powerful than CanCan, and I found it to scale better once a project needs more / more complex roles..
If you store your roles using the role_model Gem, it stores them as a bitmap in the roles_mask attribute.
This means that a user can have many roles, e.g. can be a Seller and a Buyer at the same time, and an Admin or Moderator if you like.
See:
http://railscasts.com/episodes/188-declarative-authorization
http://railscasts.com/episodes/189-embedded-association
http://railscasts.com/episodes/192-authorization-with-cancan
http://railscasts.com/episodes/193-tableless-model
And:
http://everydayrails.com/2011/10/06/rails-authorization.html
http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/
I would pick the cancan option. Is a wonderful gem, well documented, easy to use, actively mantained, and you have lots of tutorials (since it a gem created by Ryan Bates, from Railscasts, you can be sure that you have an episode for it)
I would go with the cancan option, and use the draper gem for the view logic. There are Railscasts for both gems.
http://railscasts.com/episodes/286-draper