Can I use 2 index actions in my controller? - ruby-on-rails

class MoviesController < ApplicationController
before_action :set_movie, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#movies = Movie.all
end
def current_user_movie_index
#movies = current_user.movies
end
end
I want to show all the movies for all user on index, but I want to add a new link to show only the movies for current_user. Will I do a new action? can I use the same index view for both?

Like this:
controller:
class MoviesController < ApplicationController
before_action :set_movie, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
def index
#movies = Movie.all
end
def current_user_movies
#movies = current_user.movies
end
end
routes:
resources :movies do
get :current_user_movies, on: :collection
end
It will now be available at /movies/current_user_movies.

Related

Error: Filter chain halted as :correct_user rendered or redirected

I'm working on review applications. In this app, users can post their own project and other users review to it. But I faced this error when other users try to edit their own reviews. To edit a project is only allowed to project owners. But to edit reviews should be allowed to users who wrote its review.
How can I divide this authentication?
/controllers/projects_controller.rb
class ProjectsController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update, :destroy]
before_action :set_project, only: [:show, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#project.attributes = create_params
if #project.save
redirect_to #project
else
render edit_project_path(id: #project.id)
end
end
private
def signed_in_user
unless user_signed_in?
redirect_to root_path
end
end
def set_project
#project = Project.find_by_id(params[:id])
end
def correct_user
unless current_user.projects.include?(#user)
redirect_to root_path
end
end
end
/controllers/reviews_controller.rb
class ReviewsController < ApplicationController
before_action :set_projectct, only: [:new, :create, :edit, :update]
before_action :set_review, only: [:edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
#review.attributes = create_params
if #reviews.save
redirect_to prokect_path(id: #review.project_id)
else
redirect_to project_path(id: #review.project_id)
end
end
def set_project
#project = Project.find_by_id(params[:project_id])
end
def set_review
#review = Review.find_by_id(params[:id])
end
def correct_user
unless current_user.review.include?(#review)
redirect_to root_path
end
end
end
routes.rb
resources :projects do
resources :reviews
end
If you want to verify that the user has written the review, why not compare like this:
#review.user == current_user
By the way, this is an authorization verification, so if the user does not have the right to edit the review, you should return a 403 (Forbidden) instead of a redirect.
Gems like pundit or cancancan may help you doing that properly

How can an inherited controller add to parent's before_action conditions

class ParentController < ApplicationController
before_action admin_user, only: [:create, :update]
end
class ChildController < ParentController
before_action admin_user #Also want to add :tweet
def tweet
end
end
In the ChildController we could use
before_action admin_user, only: [:create, :update, :tweet]
but then if I change anything in the parent's before_action, it won't get updated in the child.
You could call the method inside of a block inside ChildController instead of passing it by name. That would look like this:
class ParentController < ApplicationController
before_action admin_user, only: [:create, :update]
end
class ChildController < ParentController
before_action(only: [:tweet]) { admin_user }
def tweet
end
end
Unfortunately, the way ActiveSupport::Callbacks is implemented does not allow an additional action to be easily added to the configuration of the before filter. You could do this though:
class ParentController < ApplicationController
AdminActions = [:create, :update]
before_action :admin_user, only: AdminActions
end
class ChildController < ParentController
before_action :admin_user, only: superclass::AdminActions + [:tweet]
end

Authentication for not all pages Rails

I have a controller
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
http_basic_authenticate_with name: "tencet", password: "qk35lm"
def index
#posts = Post.order(:updated_at).reverse_order
end
def show
end
def new
#post = Post.new
end
end
But I don't want authentication for page "new", how can I do it?
You should specify except option because this method using before_action inside:
http_basic_authenticate_with name: "tencet", password: "qk35lm", except: [ :new ]
Here is documentation for http_basic_authenticate_with helper.

Rails, duplicate code refactoring

How can I refactor this similar snippets of code in rails controller?
app/controllers/albums_controller.rb:58…62 < >
def set_album
if current_user.admin?
#album = User.find(params[:user_id]).albums.find(params[:id])
else
#album = current_user.albums.find(params[:id])
end
end
app/controllers/articles_controller.rb:45…49 < >
def set_article
if current_user.admin?
#article = User.find(params[:user_id]).articles.find(params[:id])
else
#article = current_user.articles.find(params[:id])
end
end
app/controllers/photos_controller.rb:55…59 < >
def set_photo
if current_user.admin?
#photo = User.find(params[:user_id]).photos.find(params[:id])
else
#photo = current_user.photos.find(params[:id])
end
end
controllers/concerns/user_resource.rb
module UserResource
extend ActiveSupport::Concern
included do
before_action :set_resource , only: [:edit, :update, :destroy]
before_action :signed_in_user, only: [:new, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update, :destroy]
end
def set_resource
association = controller_name.classify.downcase
resource = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = resource.send(association.to_s.pluralize).find(params[:id])
instance_variable_set("##{association}", resource)
end
def correct_user
association = controller_name.classify.downcase
redirect_to root_path unless admin_or_current?(instance_variable_get("##{association}").user)
end
end
then, in {photos, albums, articles}_controller.rb
include UserResource
One way to do this is to create a new controller:
class ResourceController < ApplicationController
before_filter :set_resource, only: [:show, :edit, :update, :destroy]
private
def set_resource
user = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = user.send(controller_name.to_sym).find(params[:id])
instance_variable_set("##{controller_name.singularize}", resource)
end
end
then your albums_controller.rb:
class AlbumsController < ResourceController
# use #album in show, edit, update, and destroy
end
articles_controller.rb:
class ArticlesController < ResourceController
# use #article in show, edit, update, and destroy
end
photos_controller.rb:
class PhotosController < ResourceController
# use #photo in show, edit, update, and destroy
end
It would a good idea to use metaprogramming here, I mean something like:
def set_resource(association_singular) # e.g. :photo
resource = current_user.admin? ? User.find(params[:user_id]) : current_user
resource = resource.send(association.to_s.pluralize).find(params[:id]) )
instance_variable_set("##{association}", resource)
end
Then, within a controller either before_filter only: [:action] or
def action
# ...
set_resource(:photo)
# ...
end

Rails: Before process_action callback :authenticate_user! has not been defined

I'm creating a rails app that includes devise.
I'm trying to add Twilio messaging to my site with Ngrok, i used this tutorial:
https://www.twilio.com/blog/2016/04/receive-and-reply-to-sms-in-rails.html
I was able to open Ngrok in the console and get the web-id they give for my url.
I keep getting this error when I plug the url into my browser ..I'm supposed to get to my own rails local app. Not sure whats wrong.
What I added in my messaging controller made for ngrok:
class MessagesController < ApplicationController
skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_user!, :only => "reply"
def reply
message_body = params["Body"]
from_number = params["From"]
boot_twilio
sms = #client.messages.create(
from: Rails.application.secrets.twilio_number,
to: from_number,
body: "Hello there, thanks for texting me. Your number is #{from_number}."
)
#twilio expects a HTTP response to this request
end
private
def boot_twilio
account_sid = Rails.application.secrets.twilio_sid
auth_token = Rails.application.secrets.twilio_token
#client = Twilio::REST::Client.new account_sid, auth_token
end
end
really unsure what is wrong.
when its not connecting to the 'def reply' and authenticate_user should be defined by devise.
Twilio developer evangelist here.
It looks like this was a problem that Rails 5 seems to have introduced. If the filter hasn't been defined by the time it is used in a controller it will raise an error. This was discovered in the Clearance project too.
Their fix was to pass the raise: false option to skip_before_filter:
class MessagesController < ApplicationController
skip_before_filter :verify_authenticity_token
skip_before_filter :authenticate_user!, :only => "reply", :raise => false
end
I had a similar issue to this when I was working on a Rails 6 application with Devise gem for authentication and authorization.
I added a skip_before_action :authenticate_admin!, only: [:index, :show] to the Products controller
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
skip_before_action :authenticate_admin!, only: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
And it was throwing the error below when I visit the Products page:
Before process_action callback :authenticate_admin! has not been defined
Here's how I fixed it:
To use the skip_before_action :authenticate_admin!, only: [:index, :show] in the Products controller, I first needed to define the before_action :authenticate_user! in the application_controller:
# app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_admin!
end
Now I can use the skip_before_action :authenticate_admin!, only: [:index, :show] in the Products controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
skip_before_action :authenticate_admin!, only: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
An alternative, if I don't want to define the before_action :authenticate_user! in the application_controller is to use the before_action :authenticate_admin!, except: [:index, :show]:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!, except: [:index, :show]
def index
#products = Product.all
end
.
.
.
end
That's all.
I hope this helps

Resources