User model: has_many :courses
Course model: belongs_to :user
def require_course
unless #check if current user has course
redirect_to root_url
return false
end
end
i need a method that checks if current user has courses. What should i write to check if current_user has course.
I'd go for
def require_course
redirect_to root_path if #user.courses.blank?
end
Documentation about Object#blank?
How about current_user.courses.size > 0 ?
Even a shorter one:
redirect_to(root_url) if #user.courses.size.zero?
Or even shorter:
def require_course
redirect_to root_url if #user.courses.empty?
end
(note the root_url instead of root_path, as discussed here.
Related
I've got an app where users can add an organisation to their account. I want them to be able to edit their organization, and protect it from being edited by any other user. it looks like this
class OrganizationsController < ApplicationController
before_action :correct_user, only: [:edit, :update, :destroy]
private
def correct_user
#organization = current_user.organization.find_by_id(params[:id])
redirect_to root_url if #organization.nil?
end
end
models
class Organization < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
end
class User < ActiveRecord::Base
has_one :organization
end
Through Rspec I can find a record for current_user.organization. However when i call current_user.organization.find_by I receive a undefined method 'find_by'.
Can't figure out what i'm doing wrong here.
If organization is a single record, it will not respond to find_by.
Also, you're checking if organization is nil after calling a method on it. At this time it is too late. If it is nil, and you try to call find_by on it, you'll get a NoMethodError.
Instead try this:
def correct_user
if current_user.organization && current_user.organization.id == params[:id].to_i
#organization = current_user.organization
else
redirect_to root_url
end
end
As soon as the relation between Organization and User is one-to-one, you don't need to call find. #organization = current_user.organization is enough.
Sorry about my answer before. I have revised into this.
def correct_user
#organization = Organization.find_by(id: params[:id])
if current_user.id != #organization.user_id
redirect_to root_url
end
end
When current user is not owner of organization will be redirected to root_url.
An efficient way to handle this is to implement the correct_user method in the Organization controller as follows.
# Confirms the correct user.
def correct_user
#user = User.find(params[:user_id])
redirect_to(root_url) unless current_user?(#user)
end
Debug helper
Add this in your controller to see the contents of the hash at run time
flash[:info] = "Hash: #{params}"
Rails 3.2. I am using the following code to associate user_id to the record:
# review.rb
class Review < ActiveRecord::Base
belongs_to :reviewable, :polymorphic => true, :counter_cache => true
end
# reviews_controller.rb
def create
#review = #reviewable.reviews.new(params[:review])
#review.user_id = current_user.id
if #review.save
flash[:success] = 'Thanks for adding your review.'
redirect_to #reviewable
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #reviewable
end
end
I want to find a way to use this, but it keeps saying that the current_user is not defined, but I could find the current_user object:
def create
#review = #reviewable.current_user.reviews.create(params[:review]
if #review.save
flash[:success] = 'Thanks for adding your review.'
redirect_to #reviewable
else
flash[:error] = 'Error adding review, please try again.'
redirect_to #reviewable
end
end
If you can post your code to what the #reviewable object is, it might help to give a more specific answer. But if you want a one liner, you can do something like this:
#review = #reviewable.reviews.build(params[:review].merge({:user_id => current_user.id})
But personally, i think your original looks better as it's easier to read.
As an additional note, your second example also calls create and save. You don't need to call both, as create saves the object when accepting a hash of parameters. Save is nice to use if you want to initialize an object, modify it in some way, then save it later.
I think that the reason this does not work is because current_user is a method that is not defined on a reviewable.
The reviewable may belong to a user, in which case #reviewable.user.reviews.create... may be valid.
I am trying to get the current_user value into a model. I know that this is probably not appropriate since models should be kept secluded from this type of interaction but I'm running into a problem. I need to include a current_user within a method in a model and do not know how to do it.
I need to make this happen on an update in my stage controller and pass the current_user to the stage model and have that current_user value available. Any help is appreciated.
def update
if #stage.update_attributes(params[:stage])
redirect_to [#project, #stage], :notice => 'Stage was successfully updated.'
else
render :action => "edit"
end
end
You could also store the current user into Thread.current's hash.
See http://rails-bestpractices.com/posts/47-fetch-current-user-in-models
class User < ActiveRecord::Base
def self.current
Thread.current[:user]
end
def self.current=(user)
Thread.current[:user] = user
end
end
I'm learning Rails by building a shop application and I'm having a bit of trouble with redirects. I have 3 roles in the application:
Buyer
Seller
Administrator
Depending on which type they are logged in as then I would like to redirect to a different page/action but still show the same URL for each (http://.../my-account).
I don't like having to render partials in the same view, it just seems messy, is there another way to achieve this?
The only way I can think of is to have multiple actions (e.g. buyer, seller, administrator) in the accounts controller but that means the paths will look like http://.../my-account/buyer or http://.../my-account/seller etc.
Many thanks,
Roger
I've put my code below:
models/user.rb
class User < ActiveRecord::Base
def buyer?
return type == 'buyer'
end
def seller?
return type == 'seller'
end
def administrator?
return type == 'administrator'
end
...
end
controllers/accounts_controller.rb
class AccountsController < ApplicationController
def show
end
end
controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
if session[:return_to].nil?
# I'm not sure how to handle this part if I want the URL to be the same for each.
redirect_to(account_path)
else
redirect_to(session[:return_to])
end
else
#user_session.errors.clear # Give as little feedback as possible to improve security.
flash[:notice] = 'We didn\'t recognise the email address or password you entered, please try again.'
render(:action => :new)
end
end
def destroy
current_user_session.destroy
current_basket.destroy
redirect_to(root_url, :notice => 'Sign out successful!')
end
end
config/routes.rb
match 'my-account' => 'accounts#show'
Many thanks,
Roger
In UserSessionsController#create (i.e.: the login method) you could continue to redirect to the account path (assuming that goes to AccountsController#show) and then render different views according to the role. I.e.: something like this:
class AccountsController < ApplicationController
def show
if current_user.buyer?
render 'accounts/buyer'
elsif current_user.seller?
render 'accounts/seller'
elsif current_user.administrator?
render 'accounts/administrator
end
end
end
Better yet, you could do this by convention...
class AccountsController < ApplicationController
def show
render "accounts/#{current_user.type}"
end
end
If I understand you question correctly, then the solution is simple.
You can just call the method you want inside your controller. I do this in my project:
def create
create_or_update
end
def update
create_or_update
end
def create_or_update
...
end
In your case it should be:
def action
if administrator? then
admin_action
elsif buyer? then
buyer_action
elseif seller? then
seller_action
else
some_error_action
end
end
You should probably explicitly call "render" with an action name in each of those actions, though.
i have table called users, if i want to delete some user (User can add questions and add respondents (who will answer on his questions)), i need to delete him and get his id to people who deleted this person. So for example:
Sure.
def destroy_and_transfer_to(user)
transaction do
questions.each do |q|
q.update_attribute(:user_id => user)
end
respondents.each do |r|
r.update_attribute(:user_id => user)
end
destroy
end
end
Now use this method instead of the "destroy" method.
OR
you can stick to callbacks like this
before_destroy :transfer_to
attr_accessor :user_who_takes_over
private
def transfer_to
if user_who_takes_over
questions.each do |q|
q.update_attribute(:user_id => user_who_takes_over)
end
respondents.each do |r|
r.update_attribute(:user_id => user_who_takes_over)
end
end
end
Then you can :
#user.user_who_takes_over = current_user
#user.destroy
Just a couple of ideas! Good Luck!
Update: All the code i provided above belongs in your model.
In your controller you need to have a destroy method
in your controller
def destroy
user = User.find(params[:id])
user.user_who_takes_over = current_user
if user.destroy
flash[:notice] = "User destroyed, all stuff transferred"
else
Rails.logger.debug(user.errors.inspect)
flash[:error] = "Error destroying user"
end
redirect_to :back
end
Change to suite your need of course!