How do associations affect the way we create objects in the controller? - ruby-on-rails

I understand that associations are important because this gives us a way of linking two objects together so they have a 'relation' and we can query through both of them. For example,
A landing page belongs to a blog user & a blog user can have many landing pages
We obviously go into the models and apply the correct methods 'has_many' and 'belongs_to'. We also create a migration and add the foreign key to the 'belongs_to' model. This being the 'landing page'.
My problem:
When creating a landing page, it is possible to choose a blog user. This obviously passes the blog user ID into the params. I want to save this ID into the foreign key field in the landing page model.
Is this possible without doing:
def create
#landing_page = #blog_user.landing_pages.build(landing_pages_params)
end
Why do you have to go through a blog user? Another example:
def new
#landing_page = #blog_user.landing_pages.new
end
What is the purpose of doing it this way? Surely passing the ID into the field is enough without going through the blog_user?

Sure, you could do something like
#landing_page = LandingPages.new(blog_user_id: X)
Obviously, you need to know the ID of the BlogUser record. You'll then be able to access the newly associated BlogUser as you'd expect
#landing_page.blog_user #=> BlogUser(id: X)

Related

Rails 5 access profile data anywhere in session without querying database each time

I've a user profile (with name, logo, about_me) which is created after user creation(using Devise). Profile table uses user_id as Primary key.
Now I want that whenever the user creates/updates a post, while filling in form some details are taken from profile, so profile data or #profile be available in post form as I cannot expose my model in form.
To set post.myname attribute in create and #update I'm doing this:
#myprofile = Profile.find_by_user_id(current_user)
write_attribute(:myname, #myprofile.name)
I read from various sources but what's the best solution of the 4 given and if anyone can back with easy code as I do not want to do something extensive? Thanks in advance.
1)Form Hidden fields - Like get the profile data as above in hash in #edit and then pass through form and access fields in #update but that way we will pass each field separately. Can one #myprofile be passed?
2)Session - I feel if profile data is stored in a session and someone updates profile then updated data won't be available in that session.So not sure if it is plausible.
3)Caching - easy way to do that?
4)polymorphic profile---tried it but I didnot get relevant example. I was stuck with what to put as profileable id and type and how to use them in the code.
If your Profile and User models have a one-to-one relationship with each other, the simplest solution is to remove the Profile model altogether and move its fields into the User model.
Devise already queries the database to obtain the current_user object. So, your example would like this:
write_attribute(:myname, current_user.name)
Which wouldn't hit the database (after Devise has retrieved the current_user object).
If you're forced to keep the Profile model, in looking at your four scenarios ...
You could use a session variable. Something like:
session[:profile_name] ||= #myprofile.name
This would go in a controller action.
The trick here is that you will want to redefine the each relevant session variable if the profile gets updated. And because you don't have access to the session in the model, you'd be best to perform that action in the controller. So, not pretty, but it could work.
You could also use low-level caching, and save the profile relationship on the user. In general, you could have a method like this in your user model:
def profile_cached
Rails.cache.fetch(['Profile', profile.id]) do
profile
end
end
Here, too, you will have to know when to expire the cache. The benefit of this approach is that you can put this code in the model, which means you can hook its expiration in a callback.
Read more about this in Caching with Rails.
I would avoid hidden fields and I'm not sure how a polymorphic relationship would solve you not hitting the database. So, #2 and #3 are options, but if you can combine the two models into one, that should simplify it.

Different update / edit methods available to different users

I have a model Post, which is submitted and graded by different Users. The submitter and grader are identified by submitter_id and grader_id in Post model. Note that an user is both a submitter himself and a grader to others.
I want to make sure that the submitter can only edit the content of the Post but not the grade. Likewise, the grader can only edit the grade but not the content.
Is multiple edit methods the way to go? How should I accomplish this otherwise?
You can have a role column in your users table, and the role can be either submitter or grader. Not sure what you are using for authentication, but in case you are using devise, you can access the currently logged in user with current_user helper (in case you are using something else, figure this part out, or add a new helper).
Now in your update method, you can do something like this:
# Controller
# scope post to current user, so that a user cannot edit someone else's post. A crude way to achieve this is post = Post.find(params[:id])
post = current_user.posts.find(params[:id])
post.content = params[:content] if post.submitter?(current_user.id)
post.grade = params[:grade] if post.grader?(current_user.id)
post.save!
# Model - Post.rb
def submitter?(user_id)
self.submitter_id == user_id
end
def grader?(user_id)
self.grader_id == user_id
end
The advantage of keeping those methods in the model is that in case you permission logic changes (who is submitter, or a grader), you need to change it at a single location. DRY.
You can modify the above approach to show error messages, and do other similar stuff. In case you are looking for more granular authorization control, you can look into cancan gem:
https://github.com/ryanb/cancan
Your post model should only be concerned with persisting data. Better to use plain old ruby objects to encapsulate the higher order behavior of grading and submitting. Consider using service objects or form objects.
Each service or form object can then include ActiveModel::Model(rails > v4) to get its own validations.
See more about service and form objects here: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
If you only have one submit action and one grade action, its probably ok to keep in one controller. But if you start having multiple actions that are related to submitted, and multiple actions that are related to grading, this sounds like they would make great resources controllers on their own.

Modelling multiple types of users with Devise and Rails 4

I have 3 types of "users"
Shop
Provider
Customer
The last 2 users will have similar attributes such as
First Name
Last Name
and so on
And the Shop user type will have the most contrast between types.
As far as behaviours they will all be quite different, although Provider and Shop will inherent many of customer behaviours.
It seems the behaviours can be dealt with CanCan as I've researched.
I'm now attempting to how I should authenticate these types.
I have looked at the STI model but I couldn't grasp where I would these extra attributes.
My mental model is as follower:
User is a table and model
The types are abstract models that inherit from this.
So I'm wondering, how do I add attributes such as Business address for just the Shop type?
Or is it that the User Table has a column called Type and that type is associated with these type tables? And within the type tables are the extra attributes?
Don't even bother bringing Devise into this; Devise is for authentication not authorization. Authentication is determining whether or not someone who visits your site is who you think they are, like logging in. Authorization is deciding whether or not a user is allowed to perform some sort of action, like creating a new post.
What you want to do is have some sort of system that assigns a normal user your three different types; CanCan will do something like that. One way to do this on your own is using a permissions number based system. Let's say normal users have permissions level at 100, shop has a level at 50, and provider at 25. Using this system you can determine what actions a user can perform without having to make separate models, which will make your schema unnecessarily complicated. Here's an example of how this would work with say the UserController:
def show
if current_user.permissions == 100
render "customer_show"
elsif current_user.permissions == 50
render "shop_show"
else
render "provider_show"
end
end
The easiest way to do this is to add a column to the user's table called permissions that defaults to say 100 when a new row is created. Here's what that migration would look like:
def change
add_column :users, :permissions, :integer, default: 100
end
As for authenticating, don't worry about it. Let devise do it's thing; every user, no matter what type, will login and sign up in the same way, maybe just having separate forms for each that has a hidden field to set the permissions level for that specific kind of user.
I know I'm late to the party but I'm putting this out for future SO searchers. You CAN authorize actions with Devise. If you have devise models for 'buyer' & 'seller' you can add 'buyer_signed_in?' for whatever action you only want buyers to be able to do. You can also do more specific role-based authorizations as well - check out Devise' page
All in all, Tsiege's solution sounds pretty interesting. Let us know if you had any success with it!

Rails Security - Enforcing ownership at the model level

I recently coded up a 'friend' capability with my website. The way it works is if a user wants to 'friend' another user, sending a request creates a user_connection record with the original user set at the user_id and the requested user set as the contact_id. If the other user accepts the request, then another user_connection record will be made, with the user_id and contact_id reversed. If both user_connections exist, then the two users are considered friends. This currently gives each user access to any buildings shared between the two users.
I was wondering if there was some way I could enforce my user_connection model to make sure that whoever is creating the record gets set as the user_id. Otherwise it seems that someone could spoof the user_connection create request to make themself a contact of whomever they want, and then could spoof building shares using the same methodology. Are there any built in methods to prevent this?
My first thought was to have an initializer that always set the user_id to the current_user's id, but it appears that current_user is outside of the context of the model.
Don't allow user_id to be provided as a parameter, using strong params.
So, you could create the relation like that:
#friendship = current_user.friendships.new(contact_id: other_user.id)
Also make sure you provide the correct condition for current_user.
That's it... user_id is implied but never provided.

Create new page in Rails to add roles to existing users

I want to create a new page in my rails app that you can access once you're logged in. All you would see is a dropdown with the existing users and a dropdown with the role you want to assign to that user with a submit button that would add the role to the user_role column for that user. Do I do this with a
rails g controller add_roles new create
or
rails g scaffold add_roles
How do I get it to submit the correct info to the user table?
From my understanding, a rails scaffold is a full set of controller, model, and migration. In your case, I don't think you want a add_roles_controller, and an add_roles model, you just want to update a column of your existing Users DB correct?
If so, ask yourself if you really need a controller to do this, this type of functionality can be done in an existing user_controller (or something of the like). If you are going the CRUD route, you can consider this an update upon a user.
You can make an active record call from any controller, lets say you're in a user_controller and you have a users model, you could do something like:
#users = Users.all
That would return an object of all the user's stored in the db from which can can loop through them, picking out each individuals role attribute.
If you need help on creating a form, you're going to need to elaborate, this will require changing your routes to respond to a POST to a certain controller action. That controller action can then take the parameters of the post, say a user's role, and update the Users database accordingly
if you haven't yet, check out the gem devise - it's a very easy way to login/logout and it includes some pretty awesome session management
Devise
And if you want more functionality, I'd look into rolify. I haven't used it but it seems like a great way to add roles to users. Rolify

Resources