Rails API - keeping an application controller method DRY - ruby-on-rails

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

Related

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

Friendly_id preventing edit/new pages due to before_action :find_post

I am using the friendly_id gem to handle URL Slugs and when applying a fix to avoid 404's when the slug changes from the documentation, my code doesn't work properly.
The problem is that it simply redirects to the post's show view when I click on the edit button and won't let me make a new post because it "can't find post with ID..." because it's using the find_post method.
I do have the friendly_id_slugs table to store history as well.
In my Post Model:
class Post < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
...
def should_generate_new_friendly_id?
slug.nil? || title_changed?
end
end
Post Controller:
class PostsController < ApplicationController
before_action :find_post
...
def find_post
#post = Post.friendly.find(params[:id])
# If an old id or a numeric id was used to find the record, then
# the request path will not match the post_path, and we should do
# a 301 redirect that uses the current friendly id.
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end
end
I've tried using before_filter but asks me if I mean before_action and I've tried the find_post method in both the public & private section of my controller.
It sounds to me like you may want to skip that redirect logic for anything but the show action, since redirect_to #post only sends you to the show route.
def find_post
#post = Post.find params[:id]
if action_name == 'show' && request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end
Alternately, you can decouple the redirecting behavior from the pre-loading of the post with something like this:
before_action :find_post
before_action :redirect_to_canonical_route, only: :show
def find_post
#post = Post.find params[:id]
end
def redirect_to_canonical_route
if request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end

Ruby on Rails CRUD

I am building an application that allows users to create a trip. However, for some reason I am not sure if I am truly utilizing the power of rails or am I being redundant in my code. In the following example you will see a trip controller where a trip can be created and then displayed. Everything works, I just want to make sure I am going about it in the most minimal fashion.
class TripsController < ApplicationController
def new
#user = User.find(session[:id])
#trip = Trip.new
end
def create
#trip = Trip.create(trip_params)
#user = User.find(session[:id])
redirect_to user_trip_path(#user.id, #trip.id)
end
def show
#trip = Trip.find(params[:id])
end
private
def trip_params
params.require(:trip).permit(:where, :when, :price_per_person)
end
end
To tighten it up, "scope the trip to the user".
class TripsController < ApplicationController
before_filter :find_user
def new
#trip = #user.trips.build #assuming a User has many trips
end
def create
#trip = #user.trips.create(trip_params) #you may want to add an if else here to catch bad trips
redirect_to user_trip_path(#user.id, #trip.id)
end
def show
#trip = #user.trips.find(params[:id])
end
private
def trip_params
params.require(:trip).permit(:where, :when, :price_per_person)
end
def find_user
#user = User.find(session[:id]) # or current_user if you are using popular authentication gems
end
end
It's about readability too, not just less lines.

Validation on Devise on other controller

Hi maybe this is a fool question, there are info in a lot of posts, but i do not understand because im learning rails..
I have made this controller, posts_controller.rb
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
end
def create
#post = Post.new(params[:post])
#post.save
redirect_to #post
end
def new
end
end
This is now public.. How can i make this just for admins, Im using devise. this is the controller for > SecureController
class SecureController < ApplicationController
before_filter :authenticate_user!
authorize_resource
def has_role?(current_user, role)
return !!current_user.roles.find_by_name(role)
end
rescue_from CanCan::AccessDenied do |exception|
render :file => "#{Rails.root}/public/403.html", :status => 403, :layout => false
end
end
Also Registratons controller
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
if current_user.user_type == 'player'
player_steps_path
elsif current_user.user_type == 'coach'
coach_steps_path
elsif current_user.user_type == 'elite'
candidates_path
end
end
end
How can i make that domain.com/posts/new is just available for Admin, but domain.com/posts is open to everyone..
Also i see there is views for admin... how can i make domain.com/admin/posts/new to work?
Any Documentation will be nice, but also a explanation, cause as i said, im just learning rails.
thanks
Use :except
before_filter :authenticate_user!, :except => [:new]

ApplicationController how to execute code on each page

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

Resources