"permission" fields on a join table (relationship model) in rails - ruby-on-rails

I have a relationships model that holds 2 user_ids, a follower_id and a followed_id
When a user "follows" another user a relationship record is added which includes these two id's. I would like the followed user to have some say as to what the following user can see, so I am thinking of adding adding field(s) to the record, such as "followed_user_allows_follower_to_see_email_address" (it would be a less verbose)
This seems like the correct place to add these attributes, are there any reasons I should store these some place else?
Is there any reason I should not just store a list of permissions, rather than an attribute for each permission? For example followed_user_allows_follower_to "see_email_address,see_some_other_detail,yet_another_detail" vs a separate field for each permission.

Related

Creating multiple records, giving an array of params

For example, I have a model Hobby. And I have a User model, and user has many hobbies. Hobby also has many users. And I have a linking table UserHobby (belongs to user, belongs to hobby).
Some user registers on a site, picks some hobbies, and then saves this list. Technically creates an array of records. So on my backend part I have to do something like this:
UserHobby.create( user: user, hobby: params[:hobbies]), where :hobbies is an array. But it doesn't work this way.
Is there a way to do this without using something like params[:hobbies].map{ |hobby| UserHobby.create( user: user, hobby: hobby) } ?
The map is fine, but when creating associations use association methods.
params[:hobbies] is a param coming from a form, so presumably it contains an Array of Hobby IDs (perhaps better as params[:hobby_ids]). Since these Hobbies already exist in the database, we can simply append them to the user's list of hobbies. This will insert the necessary rows in the join table.
user.hobbies << Hobbies.where(id: params[:hobbies])
user.hobbies is cached and Rails will only check the database once. If you do UserHobby.create!(user: user, hobby: hobby) then user.hobbies will be out of date. If you update user.hobbies directly then user.hobbies will be updated.

How to design database tables for different types of Users?

I'm on a project with Rails, Postgresql and Active Record, where I have Users, that can be either Influencers, or Creators.
The Users have common columns such as email, password, first_name and last_name, but :
influencers will have followers, eg_rate, account columns
creators will have SIRET_number and specialty columns
How can I design my database so Influencers and Creators are kind of "child" of the Users table ? I mean, is it possible to have a db where I can access a User' followers or a User's specialty with one query, or I'll always have to do multiple queries to achieve this ? I've tried to create three tables for each, with a foreign key user_id in Influencers table and Creators table. I also tried to add a user_type column to my User table where I pass the values of either "influencer" or "creator", but I'm kind of lost on how to link every tables...
Thank you everyone.
Your approach is right.
You can create a table users with the common columns and add a foreign key to influencers and creators tables.
Then when you need to retrieve the data, you can use ActiveRecord relations to easily fetch data and use ActiveRecord's includes method for relations.
For example:
class Creator < ActiveRecord::Base
# The relation must be set
has_one :user
end
# To fetch data anywhere else:
Creator.find_by(SIRET_number: 1234).includes(:user)
If you need to retrieve a creator or influencer by an attribute from related users table, you can use joins:
Creator.joins(:users).where(users: {email: "foo#bar.com"})
Make sure you have the relations set in both User and Creator models.
Check out this topic for more info.
By using includes or joins instead of using creator.user you'll avoiding the unnecessary additional query. The downside is the syntax is now longer, you can maybe create your own getters to easily retrieve data instead of writing "includes" everytime.
Also assuming you're not aware of this method, I suggest you to read about the common N+1 problem with Rails & ActiveRecord. The same methods can solve a lot of problems for you.

set attributes in a has_many_though relationship in Rails

I have three models: Company, User and Employment. Each Company has many users though their Employments, and a user might belong to several companies through his employments.
Now, the tricky part: let's say my User1 belongs to 2 companies. He is SUPER_ADMIN in the first compnay, but just BASIC_USER in the second one. What would be the best way to define his roles ?
I used to have a simple has_many relationship between Company and User, which allowed me to simply set a is_admin attr on my User, but obviously this won't do with the new HMT relationship.
I thought of defining an array of IDs for each company, that would include the IDs of each admin user, but I'm pretty sure there is a cleaner way around.
In above scenario, you can use intermediate table i.e. Employment table to save all details of a user and it's associate company.
As Employment table will have ids of both user & company it will be easy for you to keep extra information related to user and company. Just add role column in this and use this to get information

Program design when combining external sources with local

I have a Rails app that is basically designed this way:
It has a Book model, that has an external_id (all saved Book records have an external_id). The external_id links to an external source about books that doesn't allow for the data to be stored. We use a Presenter to handle some of the differences in the Book model and the external library's class to smooth things over for the view.
We let users do things like "Favorite" their books, regardless of source, so we have a join table and model with a book_id and a user_id to record favorites.
However, in some of the queries, there will be a list of results displayed to the user from the external source, even though we might have Book records with those external_ids. We want to be able to display information like who that the user is friends with that has favorited that book.
It seems there are a couple of ways to handle this:
1) Always load the canonical Book record (if it exists) in the presenter based on the external_id, and override the Book#friends_who_favorited method to return false if no external_id was found
2) Overload the presenter to either call Book#friends_who_favorited or if not a Book record, create its own join query based on external_id (since we wouldn't know the book id yet).
3) Denormalize the database a little, and make sure that we always store the external_id everywhere -- Basically treat external_id like the primary key since every Book record has an external_id. Then the queries can be done more directly, not require a join query, and we wouldn't need multiple queries written. But, this ties us even more to that external source since now our database design will be based on external_id.
It seems like #1 might be the best way to do it, even though it would introduce an extra query to Book (Book.where(external_id: x).first), since #2 would require writing a whole set of additional queries to handle the external_id case. But, I'm open to suggestions as I'm not fully comfortable with any of these methods.
Based on the discussions, if I do that I might consider this solution:
Setup
Uniform the identifier of all books to an id instead of ActiveRecord default id. This is the current field external_id, though I would prefer to rename it without underscore, say rid represents resource id.
Use a format for internal books on rid different from external books.
For example, suppose the format of external id like "abcde12345", then you name the internal books rid as "int_123" according to actual id so all of them are guaranteed to be unique.
Use a model callback to update rid after creating. If it's internal, copy its id and add "int_" prefix. If it's external, save its external id to that field.
Usage
Now usage would be simpler. For every action, use rid instead of original id. When an user favouring the book, the association would be the rid.
In the join table, you can also keep the original id there, so that when one day you changed implementation, there would still be original ids available.
Now the join table will have 4 fields: id, user_id, book_id(the original id), book_rid.
To display the users who liked this book, no matter the book is external or not, you can now query based on the rid in join table and fulfil the job.
Refacoring
Actually refacoring on this solution should not be hard and do no harm.
Add a field rid in the join table
Build a query task to fill rid of all books. Actually it's for internal books only which has blank external_id at this moment.
Build a query to fill the rid field in join table.
Refacor associating method to specify association id, and other related methods if needed.

Rails - a model that can have two different sub items

I have a standard devise user model with the usual fields.
This is for a situation where people are either looking for a place to stay, or they have a place to stay. So I have two categories of user that a person can be. These two categories are very distinct (i.e. a person looking for a place to stay will have very different fields to a person who has a place to stay).
So a User has:
User: name, email, password, profile_id
A User can also have a Profile (i.e. they are looking for a house).
Profile: age, sexuality, religion, occupation
That's what I have at the moment. Now I need to change that slightly, so a User can have a profile OR... they can have a House (i.e. they have a house and are looking for more people):
House: price_per_week, address, etc
How best to model this in ActiveRecord? Polymorphic association of some kind?
I've found in general that polymorphic relationships don't work well over time if the objects they are modeling are even mildly different. For your case I'd recommend keeping the two objects separate.
In general, the best way is to consider the way you want to retrieve the data. For example, I'd imagine you want to access both:
#user.house
or
#user.profile
So I'd recommend beginning by setting up relationships (that can be optional) between the users table and both the profiles and houses table. I'd also add a type field that can be used to determine which of the two types the users are.
This allows users to be of either type and allows them to have both a profile and a house.
So both houses and profiles belong_to users, and users have_many (or have_one) houses and profiles

Resources