ApplicationController how to execute code on each page - ruby-on-rails

I tried to execute some ruby code on each page of my application! I putet the hole code into my application controller:
class ApplicationController < ActionController::Base
protect_from_forgery
if Setting.exists?(1)
#setting = Setting.find(1)
else
redirect_to new_setting_path
end
end
This somehow wont work! The strange thing is that when i put the hole code into my application html it works:
<body>
<% if Setting.exists?(1)
#setting = Setting.find(1)
else
redirect_to new_setting_path
end %>
What do i have to change in my application controller?

ApplicationController is the correct place, but you should put your code in a before_filter :
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :ensure_setting
private
def ensure_setting
#setting = Setting.where( id: 1 ).first or redirect_to( new_setting_path )
end
end

Related

Rails and Devise: How to redirect user back to the requested Page after successful login?

I am using the idiom described in https://guides.rubyonrails.org/v2.3/action_controller_overview.html#other-ways-to-use-filters
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter do |controller|
redirect_to new_login_url unless controller.send(:logged_in?)
end
end
Now if the signing in process is successful, how
can I examine if it was, and b)
how can I re-redirect the user to the requested controller action?
How do I do this login-process via AJAX and JSON ?
EDIT: Also I get the following Error Message
uninitialized constant ApplicationController::LoginFilter
When I use the more elaborate solution suggested in 6.2 Other Ways to Use Filters instead of the one above such that my Controller looks like this
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_return_path, LoginFilter
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
class LoginFilter
def self.filter(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in"
controller.redirect_to controller.new_login_url
end
end
end
end
Thanks
von Spotz
You can add a before_action in the application_controller.rb where you save the requested page url:
class ApplicationController < ActionController::Base
before_action :set_return_path
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
end
And then redirect the user to this url after the successful sign-in:
class SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
return root_url if session['user_return_to'].blank?
session['user_return_to']
end
end

the AbstractController::DoubleRenderError cannot be fixed with "redirect_to and return"

I got this error today when I tried to use some helper methods for the users controller:
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and
at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need
to do something like "redirect_to(...) and return".)
I put this following helpers in application_controller.rb :
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def log_in_first
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login") and return
end
end
def correct_user?
if !(current_user.id.to_s==params[:id])
session[:error]="You have no right to do this operation."
redirect_to "/"
return
end
end
end
and here is the user_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id]=#user.id
redirect_to user_path(#user)
else
render 'new'
end
end
def show
log_in_first
#user = User.find_by id: params[:id]
correct_user?
if #user
render 'show'
else
redirect_to '/login'
end
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end
As you can see I tried to use both return and and return in log_in_first and correct_user?to fix the problem but it still doesn't work. Does anyone have any ideas?
The problem is in the show action, log_in_first redirects then the show action does whatever it wants, which is redirect or render. This is causing the error.
A better solution is to use before_action for your authentication and authorization and just let the user controller actions do their thing. Something like the below.
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def authenticate_user!
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login")
end
end
def authorize_user!
unless current_user&.id.to_s==params[:id]
session[:error]="You have no right to do this operation."
redirect_to "/"
end
end
end
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:show]
before_action :authorize_user!, only: [:show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id]=#user.id
redirect_to user_path(#user)
else
render 'new'
end
end
def show
#user = User.find_by id: params[:id]
render 'show'
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end

ruby on rails skip_before_action has no affect in my application

I want to use the before_action in application_controller.rb and then some skip_before_actions to prevent some sites from being called before a user is logged in.
But the defined function in my application_controller.erb is not called ...
application_controller.erb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
layout "application"
before_action :user_logged_in, :set_locale
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
# Prüft ob ein Nutzer eingeloggt ist.
def user_logged_in
puts "HA"
if session[:user_id].nil?
flash[:error] = "error"
redirect_to :controller => :startsites, :action => :index
else
flash[:error] = "ok"
redirect_to :controller => :startsites, :action => :index
end
end
end
The puts "HA" in user_logged_in is not printed in my server console. So I think the function is not called yet .. but why?
And in some controllers I tried to use this:
class MoviesController < ActionController::Base
skip_before_action :user_logged_in, only: [:index, :show]
also not working ... why?
Thank you very much for your help.
You are trying to call through ActionController. Its not possible that way, as you built it.
ActionController::Base
-ApplicationController #your method in this controller
ActionController::Base
-MoviesController #yr trying to skip it right here
To skip it, you have to inherit like below:
ActionController::Base
-ApplicationController #yr method is here
--MoviesController #it will find that method and skip it.
Controllers
# application_controller.rb
class ApplicationController < ActionController::Base
end
# movies_controller.rb
class MoviesController < ApplicationController
end

Rails API - keeping an application controller method DRY

I have a method in my Rails application controller that I call when I am creating a new Post. I have also created an API to create a new Post. However, it seems that I need to repeat the code for my application controller method in my API BaseController. Where is the best place to put the application controller method in my Rails app so that I do not have to repeat the code for the API? Is there a way that the API base controller can inherit from the ApplicationController?
Rails app
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def foo_action(string)
return string
end
end
Rails API
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = foo_action(#post.text)
if #post.save
respond_with(#post)
end
end
end
class Api::V1::BaseController < ActionController::Base
respond_to :json
def foo_action(string)
return string
end
end
Based on #phoet's recommendation in the comments above, I moved the foo_action method to the Post model:
class Post < ActiveRecord::Base
def foo_action
string = self.text
return string
end
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
redirect_to posts_path
else
render :new
end
end
end
class Api::V1::PostsController < Api::V1::BaseController
def create
#post = Post.new(post_params)
#post.text = #post.foo_action
if #post.save
respond_with(#post)
end
end
end

Code in each controller method

I have a controller and every method of it starts with the following code:
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
return
end
I'm just wondering if it is possible to avoid code duplication in this case ?
Yes, use a before_filter
class YourController < ApplicationController
before_filter :check_user
def check_user
..
end
end
Absolutely.
class MyController < ApplicationController
before_filter :ensure_logged_in
# actions here.
def ensure_logged_in
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
end
end
end
You shouldn't need to worry about the 'return', as rails will bail out of the filter pipeline once the redirect happens.
To avoid duplication you just need to add before_filter in every controller where you want to check user authentication.
class SomeController < ApplicationController
before_filter :authenticate_user
end
then add your user authentication logic in application controller something like this,
class ApplicationController < ActionController::Base
private
def current_user
#current_user ||= UserData.find_by_login(session[:cuser]) if session[:cuser]
end
helper_method :current_user
def authenticate_user
redirect_to({:controller=> 'user_data', :action=> 'login'}, :alert => "Not authorized") if current_user.nil?
end
end
You can use current_user helper method in every controller to get current user.
Try to use before filter. This should be fine

Resources