Checking an attribute is true before executing a CRUD action - ruby-on-rails

Before any of my article controller crud actions can run (excluding index), i want to make sure that the article's active field is true.
I thought about doing this in a before_filter, but at that point #article has not been set, any ideas please?
Thanks

You could set the article in a helper method and remove some code duplication while you're at it.
class .. < ApplicationController
helper_method :current_article
def index
# your code etc..
end
private
def current_article
#article ||= Article.find(params[:id], :conditions => { :active => true }) ||
raise(ActiveRecord::RecordNotFound)
end
end
Basically you can now call current_article in your show, edit (etc) actions and views instead of #article.

You just need to do 2 before_filter.
1 with load the article and the second one to check if field exist
before_filter :load_article, :only => [:show, :edit, :update]
before_filter :has_field, :only => [:show, :edit, :update]
...
private
def load_article
#article = Article.find(params[:id])
end
def has_field
unless #article.active
redirect_to root_url
end
end

Related

What is the best way to redirect a user to login screen from 2 or more different controllers

I have this in my application_controller
class ApplicationController < ActionController::Base
before_action :login_required, :only => 'users/login'
protect_from_forgery with: :exception
protected
def login_required
return true if User.find_by_id(session[:user_id])
access_denied
return false
end
def access_denied
flash[:error] = 'Oops. You need to login before you can view that page.'
redirect_to users_login_path
end
end
I want to use the login_required for each controller def method
Is there a better way instead of this?
class UsersController < ApplicationController
before_action :set_user, :login_required, :only => 'users/login'
#before_action only: [:show, :edit, :update, :destroy, :new]
def index
login_required
#users = User.all
end
def new
login_required
#user = User.new
end
end
Is there a better way to include login_required for all controllers methods since before_action doesn't seem to work?
I don't know the motivation of your logic, so I'll just focus on how you can solve this particular problem.
You can do something like this:
In your application controller:
class ApplicationController < ActionController::Base
before_action :login_required
private
def login_required
current_params = params["controller"] + "/" + params["action"]
if current_params == "users/new" or current_params == "users/index"
return true if User.find(session[:user_id])
access_denied
return false
end
end
def access_denied
flash[:error] = 'Oops. You need to login before you can view that page.'
redirect_to users_login_path
end
end
The login_required method will just run only on users controller's index and new action, for the rest, it'll just ignore. Also you can just use User.find() and no need to use User.find_by_id()
Now, in your users_controller.rb, you don't need to mention anything about login_required, everything will happen already in application_controller before coming here.
class UsersController < ApplicationController
before_action :set_user, :only => 'users/login'
#before_action only: [:show, :edit, :update, :destroy, :new]
def index
#users = User.all
end
def new
#user = User.new
end
end
Firstly, I'm going to suggest that you use devise for authentication, it's a lot more secure and should deal with this for you.
As for your problem, you should be able to specify the before_action like this:
before_action :set_user, :login_required, only: [:new]
Which you can put in your UserController. However if you want this globally, just put it in the ApplicationController, without the only: key.
If you want to require login for all pages except /users/login, then you almost have it right except you are specifying only: when you should be using except::
class ApplicationController < ActionController::Base
before_action :login_required, except: 'users/login'
...
end
This configuration will be applied to all sub-classes of ApplicationController as well.

Displaying item with a specific parameter in the index

I am building a website for poetries.
There are two different type of poetries: famous or amateur.
I built the CRUD functions to display all the poetries (famous and amateur, without distinction) and this is working as intended (see the PoetrisController code below).
Now, I want to give the possibility to the user to choose if he wants to see only the amateur poetries or famous ones.
Basically the user clicks the link "Amateur" or "Famous" in the navbar and he is redirected to a new page listing only amateur or famous poetries.
My question is: should I create another Controller (for example PoetriesFamousController) and creating a index function inside it to display only the famous poetries or there is a way to use the already existing PoetriesController to show only the "famous poetries" if the user clicks the link in the navbar?
PoetriesController:
class PoetriesController < ApplicationController
skip_after_action :verify_authorized, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
skip_before_action :authenticate_user!, only: [:home, :about, :newsletter, :disclaimer, :new, :create]
before_action :set_poetry, only: [:show, :edit, :update, :destroy,]
before_action :authenticate_user!, except: [:index, :amateur_poetries]
def index
if params[:search]
#poetries = policy_scope(Poetry).search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
def show
authorize #poetry
end
def new
#poetry = Poetry.new
end
def create
Poetry.create(poetry_params)
redirect_to poetries_path
end
def edit
authorize #poetry
end
def update
#poetry.save
redirect_to poetry_path(#poetry)
end
def destroy
#poetry.destroy
redirect_to poetries_path
end
private
def poetry_params
params.require(:poetry).permit(:title, :author, :body, :poster, :country)
end
def set_poetry
#poetry = Poetry.find(params[:id])
end
end
Poetries.rb
class Poetry < ApplicationRecord
def self.search(search)
where("lower(title) LIKE ? OR lower(author) LIKE ? OR lower(country) LIKE ? OR lower(born) LIKE ?", "%#{search}%", "%#{search}%", "%#{search}%", "%#{search}%")
end
end
Routes.rb
get 'poetries', to: 'poetries#index', as: :poetries
get "poetries/new", to: "poetries#new"
post "poetries", to: "poetries#create"
get "poetries/:id/edit", to: "poetries#edit"
patch "poetries/:id", to: "poetries#update"
get 'poetries/:id', to: 'poetries#show', as: :poetry
delete "poetries/:id", to: "poetries#destroy"
Here is some idea for your problem
In your view (sample idea)
poetries type:
<%= select_tag :poetries_type, options_for_select(["Famous","Amateur"]), include_blank: true, :class => 'form-control' %>
in your controller:
def index
if params[:search]
if params[:poetries_type] == "Famous"
#poetries = Poetry.famous.search(params[:search]).order("created_at DESC").limit(30)
elsif params[:poetries_type] == "Amateur"
#poetries = Poetry.amateur.search(params[:search]).order("created_at DESC").limit(30)
else
#poetries = Poetry.search(params[:search]).order("created_at DESC").limit(30)
end
else
#poetries = policy_scope(Poetry).order("RANDOM()").limit(30)
end
end
Poetries.rb, add two scope for famous an amateur
def self.amateur
where("poster != ?","Admin")
end
def self.famous
where("poster = ?","Admin")
end
The simplest thing would be to add two more actions to your controller.
def famous
#poetries = #get the famous ones
render :index
end
def amateur
#poetries = #get the amateur ones
render :index
end
Then update your routes
get 'poetries', to: 'poetries#index', as: :poetries
get 'poetries/famous', to: 'poetries#famous'
get 'poetries/amateur', to: 'poetries#amateur
# rest of the routes

Permission in rails

I have a jobs pages where users can create new jobs edit and destroy their jobs and I want to let user edit or destroy only their post only if are connected else they will returned to the job show page for this i have this code in my job controller
def require_login
#job = current_user.jobs.find_by_slug(params[:id])
redirect_to job_path if #job.nil?
end
before_action :login_required
def login_required
redirect_to new_user_session_path unless user_signed_in?
end
before_action :login_required, :require_login, only: [:edit, :update, :destroy]
the only part where this don't work is when i not connected and try to edit my job it redirect me to the log in form but after login it redirect me to the home page instead of the edit page
First of all, the code block you pasted looks weird, for three reasons:
Indentation is wrong
You have before_action :login_required twice and it's not even the same
From the methods names it's unclear what you want exactly
From what you wrote, you want a user to edit or destroy a job only if they are logged in - otherwise you want to send them to the jobs index page. If that's right, the code in your controller should look like this:
class JobsController < ApplicationController
before_filter :require_login, :only => [:edit, :update, :destroy]
def edit
# your code here
end
def update
# your code here
end
def destroy
# your code here
end
private
def require_login
redirect_to job_path unless user_signed_in?
end
end

Update route in rails doesn't respond well to after_action?

class FrogsController < ApplicationController
before_action :find_frog, only: [:edit, :update, :show, :destroy]
after_action :redirect_home, only: [:update, :create, :destroy]
def index
#frogs = Frog.all
end
def new
#ponds = Pond.all
#frog = Frog.new
end
def create
#frog = Frog.create(frog_params)
end
def edit
#ponds = Pond.all
end
def update
#frog.update_attributes(frog_params)
end
def show
end
def destroy
#frog.destroy
end
private
def find_frog
#frog = Frog.find(params[:id])
end
def frog_params
params.require(:frog).permit(:name, :color, :pond_id)
end
def redirect_home
redirect_to frogs_path
end
end
Hi all. I was wondering if someone could explain to me why the update route in rails can't take my after_action of redirecting (custom made method on the bottom) it home. The error that I get when i include update in the after_action is "Missing template frogs/update".
This is going to cause me to manually add a redirect_to frogs_path inside the update method.
thanks!
The after_action callback is triggered after the action has run its course. You cannot use it to render or redirect. Do that within the action itself by calling the method:
def update
...
redirect_home
end

Is this too DRY, am I going overboard?

I've been drying some code, one of this refactors is as following:
I have 3 controllers ( ConstructionCompanies, RealEstateCompanies, People) all of which had the following pattern:
class ConstructionCompaniesController &lt ApplicationController
before_filter :correct_user, :only => [:edit, :update]
private
def correct_user
#company = ConstructionCompany.find(params[:id])
if(current_user.owner != #company.user)
redirect_to(root_path)
end
end
class RealEstateCompaniesController &lt ApplicationController
before_filter :correct_user, :only => [:edit, :update]
...
private
def correct_user
#company = RealEstateCompany.find(params[:id])
if(current_user.owner != #company.user)
redirect_to(root_path)
end
end
As you can see the correct_user is repeated in each controller.
So what I did inside I helper that is included for all of them I created a method:
def correct_user_for_seller_of_controller(controller)
#"User".classify will return the class User etc.
#seller = controller.controller_name.classify.constantize.find(params[:id])
redirect_to(root_path) unless (current_user == #seller.user)
end
Know inside each controller I have:
class ConstructionCompaniesController &lt ApplicationController
before_filter :only => [:edit, :update] do |controller| correct_user_for_seller_of_controller(controller) end
class RealEstateCompaniesController &lt ApplicationController
before_filter :only => [:edit, :update] do |controller| correct_user_for_seller_of_controller(controller) end
I like the fact that is DRY now, but the problem is that it seems a little to complex for me, hard to understand. Did I went too far ?
Add the correct_user method to the ApplicationController class.
class ApplicationController
def correct_user_for_seller_of_controller
#"User".classify will return the class User etc.
#seller = controller_name.classify.constantize.find(params[:id])
redirect_to(root_path) unless (current_user == #seller.user)
end
end
In your controllers use the new method as a filter method:
class RealEstateCompaniesController < ApplicationController
before_filter :correct_user_for_seller_of_controller, :only => [:edit, :update]
end
It's definitely good to clean that up. I think you may have made things slightly more complex than necessary, though, what with all the Proc whatnot.
If the filter is being run on an instance of the controller, then there's no need to pass the controller to itself. Just call controller_name within the method and you're good to go.
I'd personally DRY it a bit more and move the filter to a superclass (or AppplicationController).
The method itself could also definitely be simplified. Use some introspection for example.

Resources