Rails 4 before_action for HTTP Post - ruby-on-rails

I have a before action on a create method like so:
class ApplicationController
before_action :authenticate_user!
end
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to post_path(#post)
else
redirect_to root_path
end
end
end
#on some view while logged out, a user may see a form like so
<%= form_for :post, url: posts_path, method: :post do |f| %>
# some post data
<%= f.submit %>
<% end %>
I want to be able to be a logged out user and view a form that allows the guest to post. When the guest clicks the form submit button, I would like it to render the login form and after successful login to proceed to the create method.
Everything is working up until the point of proceeding to the posts#create method. Instead, it is rendering root_path. I am unsure if this is part of the desired functionality of before_action or not.
So, is it possible to have a before_action continue to a HTTP Post method with the original params from the form?

If you are using devise, you could add something like this to your ApplicationController
def after_sign_in_path_for(resource)
redirect_to request.referrer
end
This will ensure that every time after signin, you refer to the previous page.

Related

how can destroy all task linked to login user in rails

I am trying to delete all task that is linked to logged in user but when I click on delete all button it shows the error
No route matches [POST] "/tasks/destroy_all"
task_controller.rb
class TaskController < ApplicationController
def all_destory
#user = current_user
#user.tasks.destroy_all
redirect_to user_tasks_path
end
end
route.rb
get '/tasks/destroy_all', to: 'task#all_destory', as: :destroy_all
HTML
<% #tasks.each do |task| %>
<%= task.daily_task %>
<%= task.date %>
<% end%>
<%= button_to "delete all", destroy_all_path %>
When destroying records you want to use the DELETE HTTP verb.
GET requests are saved in the browsers history and should not create, modify or destroy anything on the server.
Typically in Rails you just have a route to destroy a single record. But if DELETE /things/1 deltes a single resource then DELETE /things should logically destroy the entire collection:
get '/user/tasks', to: 'users/tasks#index', as: :user_tasks
delete '/user/tasks', to: 'users/tasks#destroy_all'
# app/controllers/users/tasks_controller.rb
module Users
class TasksController < ApplicationRecord
before_action :authenticate_user!
# display all the tasks belonging to the currently signed in user
# GET /user/tasks
def index
#tasks = current_user.tasks
end
# destroy all the tasks belonging to the currently signed in user
# DELETE /user/tasks
def destroy_all
#tasks = current_user.tasks
#tasks.destroy_all
redirect_to action: :index
end
private
# You don't need this if your using Devise
def authenticate_user!
unless current_user
redirect_to '/path/to/your/login',
notice: 'Please sign in before continuing'
end
end
end
end
<%= button_to "Delete all", user_tasks_path, method: :delete %>
Your HTTP verb and your route must match. Currently your button is using POST, but your route accepts GET. You could change them both to POST.
post '/tasks/destroy_all', to: 'task#all_destory', as: :destroy_all
This fixes the problem in the question, but it's not ideal. As #max points out, DELETE would be more communicative of what clicking the button does– delete resources.
DELETE documentation

Edit and update of user info. in RoR

I am using devise for user management so it let's user sign up with default email and password fields.
I added new fields/columns into the user model say username, designation and company.
So I have a profile view say with route '/users/1' and a link_to helper which would allow me to edit and update my user info.
By default i can only use users/edit route to edit my user info. How can i manage a new or separate edit and update option with different route say '/users/1/edit' from my profile view.
I read some posts before this but didn't help me. If anyone could outline things i should do. Thanks for reading :))
Edit:
routes file
root 'public#index'
devise_for :users
resources :users do
put 'users/:id/edit', to: 'users#edit'
end
user controller
class UsersController < ApplicationController
before_action :authenticate_user!
after_action :verify_authorized
before_action :set_user, only: %i[ show edit update ]
def index
#users = User.all
authorize User
end
def show
authorize #user
end
def edit
if current_user == #user
#user.update()
end
end
def update
authorize #user
if #user.update(secure_params)
redirect_to users_path, :notice => "User updated."
else
render 'edit'
end
end
private
def secure_params
params.require(:user).permit(:designation, :company,
:username)
end
def set_user
#user = User.find(params[:id])
end
end
In my view to go to edit:
<% if current_user.id == #user.id %>
<%= link_to 'Edit My profile', edit_user_path(#user), method: :edit,
class:"btn btn-primary" %>
<% end %>
If you really want to have a route user/:id/edit and not use the Devise default users/edit route(which edits the currently logged-in user). You can do the following:
Let's assume you have a users controller(if you don't have one, create one) and add an edit action to it which will handle the editing logic:
class UsersController < ApplicationController
# other code
def edit
user = User.find_by(id: params[:id]) # this id will be passed through the route
# Now here you need some authorization logic to prevent users from updating others.
# If you use CanCanCan, Pundit or any other authorization gem then write
# this logic there
if current_user == user
user.update() # do your update logic here with params you have
# render some json or whatever you want
else
# render some error messages in format you are using
end
end
end
This is the controller logic, now in your routes.rb file you need to register this route:
put 'user/:id/edit', to: 'users#edit'
This will edit the user with ID specified at :id.
Note again: This is not the approach I would take, I would rather just use the users/edit route and update the currently logged in user, but you wanted an example of this so do as you will

Render simple_form from partial view on application.html.erb

I want to create a partial view for my registration form and add it to the application layout file because it will be shown in the navigation bar in a dropdown menu.
How can I create this form in a partial view using simple_form gem and render it on application.html.erb?
<%= simple_form_for(#user, url: account_register_path) do |f| %>
Considering that the code above is the way to create the form and I don't know where I should define #user to be used in the application layout nor if I really need it.
Can someone please clarify this?
Don't put it in a partial, just have the registration view on its own, called with render...
#app/views/layouts/application.html.erb
<%= render "accounts/new" %>
#app/views/accounts/new.html.erb
<%= simple_form_for :user, url: account_register_path do |f| %>
...
Whilst you can use a symbol to populate form_for, it won't include the attributes of the model, or the various hidden methods which give context (such as id etc).
If you wanted to populate the accounts#new view/action with a variable, you'll have to set it in your ApplicationController:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_user
private
def set_user
#user = User.new
end
end
This would allow you to use:
#app/views/accounts/new.html.erb
<%= simple_form_for #user, url: account_register_path do |f| %>
--
The best way to implement this would be with Ajax -- this way, the #user object is only created when you need it (resolves bloat).
We've done this before:
You'd just need the following:
#app/views/layouts/application.html.erb
<%= link_to "New User", account_register_path, remote: true %>
#app/controllers/accounts_controller.rb
class AccountsController < ApplicationController
respond_to :js, :html
def new
#user = User.new
respond_with #user
end
end
#app/views/accounts/new.js.erb
$("<%=j render 'accounts/new' %>").appendTo("body");
You have to define #user in the corresponding controller action (I would assume from your code, that controller is called account and action is register), it would be something like:
#user = User.new
In order to initialize instance being created/updated. Then, attributes used in all fields should match users database table.

Rails 4: setup custom redirect_to in link_to helper with remote: true

In my Rails 4 app, I have a calendar and a post models: a calendar has_many posts and a post belong_to a calendar.
In Calendars#Show, I display all the posts that belong to this calendar.
In this very same view, I use the following link to update the approval custom attribute of a post:
<%= link_to post_path(:id => #post.id, "post[approval]" => "ok"), :method => :patch %>
Problem, once I click this link, I am taken to the Posts#Show view of this post.
What I would like instead, is to remain on the same Calendars#Show view.
I thought of implementing a redirect_to :back in my Posts#Update action in the controller, but this is not possible, since this would redirect me to the Posts#Edit view when I update a post from this very same Posts#Edit view.
So, I am looking for a solution to implement a redirect_to specifically for my link_to helper with remote: true from the Calendars#Show to the Calendars#Show.
Is that possible?
with remote: true
If you're using remote (your code snippet does not have it), you won't be redirected anyway:
<%= link_to post_path(:id => #post.id, "post[approval]" => "ok"), :method => :patch, remote :true %>
The remote: true functionality invokes ajax, which is an XML call (through JS) to your server. By virtue of the call being out of "scope" (IE not a standard "request"), it will not cause any changes to your current browser state, unless you trigger them yourself:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
#post = Post.find params[:id]
respond_to do |format|
if #post.update
format.json #-> fired only when json is datatype
format.js #-> invokes app/views/posts/update.js.erb
format.html { redirect_to #post } #-> only fires on http request
end
end
end
end
#app/views/posts/update.js.erb
## do what you want here
from the Calendars#Show to the Calendars#Show
If you want to refresh your page, you could use:
#app/views/posts/update.js.erb
window.location.reload(true);
--
To give you some more context, ajax stands for asynrhconous javascript and xml. This means that each of the requests you send with it are considered to be asynchronous -- or out of scope of the "normal" HTTP request.
Ajax requests are meant to be used to apply changes / updates to an already loaded page. A good example would be a chat system or something.
Your question of how to redirect after an Ajax response, although not wrong, is definitely against convention of how it's considered to work. If you wanted to create a redirect as you are attempting to, I would highly recommend using a standard HTTP request with some conditions in your action:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
#post = Post.find params[:id]
#post.update post_params
redirect_to posts_path if #post.approved.changed?
redirect_to root_path if ....
end
end
Since this is an AJAX request, in your Post controller you need to respond to js:
def update
# ...
respond_to do |format|
if #post.update(post_params)
format.js { head :ok } # see here no redirects
# ... other formats
else
# ...
end
end
end

How to restrict edit action to only current user

How can you make a user edit action only available if the user is current user? I am using devise.
Devise has this:
before_action :authenticate_user!, only: [:new, :edit, :update, :destroy], notice: 'you must sign in first!'
But all this does is make sure a user is logged in not if a user is equal to current user? I want to make sure other users aren't able to edit other users accounts.
What is the best way to do this? Should I create a new before_filter? I couldn't find any standard way.
You can use the current_user method provided by devise. Here you can read more -current_user method.
def edit
unless current_user
redirect_to home_path, :alert => "Restricted area"
end
end
I highly advise looking into the CanCanCan gem to handle these things. In such a case your code would look something like:
View:
<% if user_signed_in? %>
<% if can? :update, #user %>
# Edit something
<%= link_to edit_profile_path(#user), class: 'user' do %>
Edit your profile
<% end %>
<% end %>
<% end %>
And in your Users controller or such you would add the following line which would take care of the case where a user manually types a url unto the browser:
Controller:
class UsersController < ApplicationController
load_and_authorize_resource
...
More info and docs: https://github.com/CanCanCommunity/cancancan

Resources