Alright so this is another try at asking my question. I did rather poorly the first time. I have a relationship set up in my models so that users have many submissions and submissions belong to a user. I have a reference column in my submissions table that references :user and an index:
add_index :submissions, :user_id
I am using omniauth so that people can sign in with twitter. I simply want to be able to have a signed-in user be able to submit a submission and then have rails understand that there is a relationship between the current_user and the submissions they just wrote. Problem is I can't seem to store current_user in an instance variable like so:
#user = current_user
which I would like to use in my submissions controller (create) like so:
#user = current_user
#submission = #user.submissions.create(params[:submission])
I need this so that I could have a user create a submission on views/submissions/index.html.rb page and rails would understand the relation. Thank you so much for looking at this. Also I apologize in advance if I just missed something really obvious or that is common knowledge.
Your post does not really contain a question or did I miss something? What seems strange to me is that you want to assign #user = current_user. There is no need to do so, current_user should be a user anyway, therefore you could just write #submission = current_user.submissions.create(params[:submission]).
Maybe you can edit your post and provide more details on your helper methods (current_user), the error message you get with the code above. What do you get if you add Rails.logger.info current_user (or current_user.name if you have this field)?
If you want you can follow the link in my profile here on Stack Overflow, I have a couple of Rails tutorials there, including one with Omniauth.
Related
I am trying to implement a retweet (repost) function for a Twitter clone. I initially thought I might need a separate retweet model, but I want to try and implement it using my existing tweet (micropost) model.
I'm having trouble wrapping my head around the schema and what migrations I would need to run. A Micropost would need a Repost column, but would a user need a repost_id? So I would add a Repost column to Micropost, and it would reference User? Or should I actually be adding the column to the User model?
Also if the repost function would work similar to create, would it not?
def repost
#micropost = Micropost.find(params[:id])
#repost = #micropost.repost by current_user
if #repost.save
flash[:success] = "Repost created!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
Is there any way to edit my current Micropost model, or do I need to utilize a specific Repost model?
The way I go about is asking if I need a one to many or a many to many relationship between reposts and users. A user can have (make) many reposts and a repost can be reposted by many users, so that would be a many to many relationship. Meaning you need a joint table between users and micropost - and the optimal name for it in this case is repost.
If you want to think about it the other way round - the way you proposed it: if you would reference the user id in the micropost table in a user_id column, there wouldonly be "enough space" to save one user id... so that would be a one to many association, which doesn't make that much sense for reposts.
For your question about the method, you will need to create a new repost instance once a user clicks on a button (submits a form) to repost. You should have a look at nested resources: https://guides.rubyonrails.org/routing.html#nested-resources
I have two models: User and Dream. User has_many :dreams and Dream belongs_to :user. I have two controllers for users and dreams as well. I want to create new Dream with form_for with a reference to a particular User. But it shouldn't be specified by me, it should somehow (and this is where I'm stuck) track on which User's page I pressed "Create new dream", and create it for this User.
I managed to do this with using only Users Controller (for managing Dreams as well), with passing user_id parameter in URL, and with hidden field. But I realize this is not a great way to do it, because anybody can edit URL or hidden field's value and create new Dream for any User.
I'm looking for a way to make two controllers communicate with each other under the hood. Or maybe this is not what I need and I miss some conceptual nuance of MVC or whatever. I need someone to push me in the right direction.
If you have login functionality, then you can define a method in ApplicationController that returns the user that matches the session token provided by the request, and then do current_user.dreams.create(params) which will create the dream with reference to whichever user is returned by current_user and never put any information about the user into the client's view.
If you don't have login functionality, then I wouldn't worry about the fact that it can be edited, since at that point anyone would be able to create a dream for any other user anyway just by visiting that page.
edit: e.g. I've often used something like this:
def current_user
#current_user ||= User.find_by(session_token: session[:session_token])
end
You can add an optional user_id param to the new action in the DreamsController. It that param build the new dream on that User.
def new
user_id = params[:user_id]
#dream = if params[:id].present?
User.find(params[:id]).dreams.build
else
Dream.new
end
end
Then your link will be something like
<%= link_to "Create New Dream", {:controller => "dream", :action => "new", user_id: user[:id]}%>
I think that is the way things are done of such kind.
For:
anybody can edit URL or hidden field's value and create new Dream for any User
If you have single user through out the app, he can do anything ; he is superAdmin
If multiple users
they must have different roles like; moderator, superadmin, admin
in such case; authentication alone cannot do all the things you have to use some method of authorizationmechanism like cancan, cancancan, pundit,etc.
these basically prevents any user to do something nasty they are unauthorized.
If any user modifies the hidden field value, you have to check if he is authorized to do so or not.
I'm putting together a side project for a teacher/student type of website where the student will share a dashboard with the teacher. The teacher and student can both upload files and leave comments in the dashboard.
The part that I'm stuck on is the permission. For student, I've set up the index controller to this method
def index
#Homework = Homework.where(:user_id = current_user)
end
With this, I'm able to have the student only see the work that they have, but I'm confused on how to get the teacher to see each individual student's work?
Suggestions? Thanks
Here's a simple solution if you only ever need to support a single class in your application:
def index
if current_user.teacher?
#homeworks = Homework.all
else
#homeworks = Homework.where(user_id: current_user)
end
end
Otherwise, your Homework schema does not seem to be correctly designed. See your query below:
Homework.where(:user_id = <student_id>)
This works to retrieve a student's homeworks, but it does not work to retrieve a teacher's students' homeworks. You may need a class_id for a teacher to see each individual student's work:
Homework.where(:class_id = <class_id>, :user_id = <student_id>)
A Class has_many Teachers and has_many Students. This design will allow you to support multiple classes.
Some more guiding questions:
Is teacher/student both kept in the same User model?
How do you differentiate between teacher/student in your current User model?
Is there a "user_type" column somewhere in User?
What happens if the current_user is of the "teacher" user_type?
For complex user permissions, use CanCanCan: https://github.com/CanCanCommunity/cancancan
Don't use uppercase instance variables. ex: #Homework should be #homework
Check out the gem CanCan. Install it (follow the instructions, you should have to put something in application controller), Then, put in your ability file:
class Ability
include CanCan::Ability
def initialize(user)
can :manage, Homework, user_id: user.id
end
end
Then at the top of your StudentController put
load_and_authorize_resource
And the index action should look like:
#homework = #student.homework
Now, you didn't post your whole controller so this is a much as I can help.
I believe you may have a bigger underlying issue. You have students and teachers has_many homework i read in your comment. Then in your example you use user_id. You are likely overriding your students and teacher ownership of homework. You would need a has_and_belongs_to_many relationship OR you would need a student_id and teacher_id columns on the homework table
Cancan automatically generate a number of instance variables which can make it feel like magic. Watch the free railscasts on cancan the same guy who made the video wrote the CanCan library.
I've started to implement a new project using Devise, which is pretty fantastic for handling users. However, when a user signs up, they're not just creating a User model, but also need to create a related Account model that represents the company. Additional users will also belongs_to this Account model.
I can't seem to find a hook for this in Devise, though it seems like a pretty common pattern. What's the best practice for this?
I should also mention that there are a couple of fields for the Account that need to be provided on the sign_up form, so just something like this in the User model:
after_create :make_sure_account_exists
def make_sure_account_exists
if self.account.nil?
#account = self.create_account({ :company_name => '???' })
end
.. as I'm not sure how to get the company name.
I've been reading up on rails security concerns and the one that makes me the most concerned is mass assignment. My application is making use of attr_accessible, however I'm not sure if I quite know what the best way to handle the exposed relationships is. Let's assume that we have a basic content creation/ownership website. A user can have create blog posts, and have one category associated with that blog post.
So I have three models:
user
post: belongs to a user and a category
category: belongs to user
I allow mass-assignment on the category_id, so the user could nil it out, change it to one of their categories, or through mass-assignment, I suppose they could change it to someone else's category. That is where I'm kind of unsure about what the best way to proceed would be.
The resources I have investigated (particularly railscast #178 and a resource that was provided from that railscast), both mention that the association should not be mass-assignable, which makes sense. I'm just not sure how else to allow the user to change what the category of the post would be in a railsy way.
Any ideas on how best to solve this? Am I looking at it the wrong way?
UPDATE: Hopefully clarifying my concern a bit more.
Let's say I'm in Post, do I need something like the following:
def create
#post = Post.new(params[:category])
#post.user_id = current_user.id
# CHECK HERE IF REQUESTED CATEGORY_ID IS OWNED BY USER
# continue on as normal here
end
That seems like a lot of work? I would need to check that on every controller in both the update and create action. Keep in mind that there is more than just one belongs_to relationship.
Your user can change it through an edit form of some kind, i presume.
Based on that, Mass Assignment is really for nefarious types who seek to mess with your app through things like curl. I call them curl kiddies.
All that to say, if you use attr_protected - (here you put the fields you Do Not want them to change) or the kid's favourite attr_accessible(the fields that are OK to change).
You'll hear arguments for both, but if you use attr_protected :user_id in your model, and then in your CategoryController#create action you can do something like
def create
#category = Category.new(params[:category])
#category.user_id = current_user.id
respond_to do |format|
....#continue on as normal here
end
OK, so searched around a bit, and finally came up with something workable for me. I like keeping logic out of the controllers where possible, so this solution is a model-based solution:
# Post.rb
validates_each :asset_category_id do |record, attr, value|
self.validates_associated_permission(record, attr, value)
end
# This can obviously be put in a base class/utility class of some sort.
def self.validates_associated_permission(record, attr, value)
return if value.blank?
class_string = attr.to_s.gsub(/_id$/, '')
klass = class_string.camelize.constantize
# Check here that the associated record is the users
# I'm leaving this part as pseudo code as everyone's auth code is
# unique.
if klass.find_by_id(value).can_write(current_user)
record.errors.add attr, 'cannot be found.'
end
end
I also found that rails 3.0 will have a better way to specify this instead of the 3 lines required for the ultra generic validates_each.
http://ryandaigle.com/articles/2009/8/11/what-s-new-in-edge-rails-independent-model-validators