I have a page composed by several partials (3).
One of them is a forum and when a user submit that forum, rails calls a create of a controller. After the object is created, I have to render the same page.
How is possible from the controller to recall directly the page with the partials already embedded ? Is it possible to specify manually the partial I want in the controller (I don't care if I violate the MVC paradigm).
Tnx
Try adding something like this to your application controller (this code is from authlogic gem):
def store_location
session[:return_to] = request.request_uri
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
Then, in the controller that renders the 3 partials, call store_location. Then, in the controller that creates the forum post, call redirect_back_or_default.
Hope this helps,
- Dave
Related
I have a button that allows a person to join a group (called a pod in my application). The button uses remote: true to do an AJAX call. I have a join table that manages the relationships between users and pods with has_many: though:. In the join table controller I want to be able to check if the user is authenticated before that user can join a pod. If not authenticated, the user should be taken to a log in page and then back to the page with all the pods listed.
Right now, I've got the following code in pod_users_controller:
before_action :user_logged_in
...
private
def user_logged_in
render :js => "window.location = '#{new_user_session_path}'" unless user_signed_in?
end
In my application controller I have the following code to redirect the user back to the page they were on before authenticating:
def store_location
session[:previous_url] = request.fullpath unless request.fullpath =~ /\/users/
end
def after_sign_in_path_for(resource)
session[:previous_url] || root_path
end
It's kind of works but it feels very flaky. For example, I have another button which allows you to create a new group with an AJAX bootstrap modal. I've tried using the same method with before_action :user_logged_in in the pods_controller file. It pops the modal without the form and then redirects. Or when I click on the join button it will redirect me to the form to create a new pod if I just clicked the "start a pod" button before that.
My question is, is there a better way to do what I'm doing? How do I redirect to login before the modal pops? I'm just learning ruby and rails so any best practices would be much appreciated. Also, this is a total side question but in this approach I have the same method I'm calling "user_logged_in" from two controllers. Right now I have that method at the bottom of both. I'm assuming there's a place I can put it to make it accessible by multiple controllers from one place?
Thank you!
I'm on Rails 4.2 and Devise 3.4
Devise comes with these helpers: https://github.com/plataformatec/devise#controller-filters-and-helpers
That includes the before_action :authenticate_user! as well as the user_signed_in? helper (almost the same name as yours), which is available throughout the app. You could use user_signed_in? to redirect from within a view with ERB or in a controller.
In one of my controllers I have this method:
def method_name
if current_user
#model = Model.find(params[:id])
if #model.destroy
flash.alert = 'Model deleted successfully'
redirect_to models_path
end
end
end
I check if there is a current_user assigned by devise before giving the ability for the #model to be deleted. Is this safe and sufficient in terms of security?
What I really do is just checking if current_user exists. So is there a way that somebody can "trick" the system that current_user does exist and as a result be able to trigger the commands included in the method?
You will get a spectrum of answers in this. But if you want the user to be logged in then just do this at the top of your controller:
before_filter :authenticate_user!
That is provided by devise and ensures that there is a logged in user before allowing any controller actions.
If you have simple authorization then yes, most likely though you are going to want to make sure that the user has the authorization to delete the object. You can do that several ways. My favorite one right now is the Pundit gem.
You could also just check that the user owns the object in order to be able to delete it. That code would look something like this
#model = Model.find(params[:id)
if current_user.id == #model.user_id
# Rest of your destroy code
end
I just started to learn Rails and I cannot understand that:
In my Post controller I do not have method show (not described), but I put in my controller that:
def method_missing(name, *args)
redirect_to posts_path
end
I think that if controller couldn't find action show - it would call method_missing and after that redirect to index method, but Rails tries to render view show.html.erb.
Why is method missing not catching? How can I use method_missing?
Rails does not require action to be present in controller if corresponding template exists. It just assumes empty action and renders template, that is why your method_missing isn't invoked.
If you don't need show action anyway - just remove show.html.erb and method_missing will work as expected.
What's the best practice for redirecting the user, using Devise, back to the page she is currently on after she logs out?
The devise docs say to override the following (in your application controller):
def after_sign_out_path_for(resource_or_scope)
# logic here
end
Which is easy enough. However, I'm setting the previous page to be a session variable, like this:
session[:return_to] = request.fullpath
The problem is that when you sign out, the session is destroyed, and the top method occurs AFTER the session is destroyed, meaning you no longer have access to it. I'm thinking of putting it in a class variable or something similar, but wanted to see what SO thought.
If you are always using the page where the logout link was clicked you could use the referrer on the request.
def after_sign_out_path_for(resource_or_scope)
request.referrer
end
if you want to redirect user to sign in page after sign out write the below function in you application controller
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
I need to call the create action in controller A, from controller B.
The reason is that I need to redirect differently when I'm calling from controller B.
Can it be done in Rails?
To use one controller from another, do this:
def action_that_calls_one_from_another_controller
controller_you_want = ControllerYouWant.new
controller_you_want.request = request
controller_you_want.response = response
controller_you_want.action_you_want
end
You can use a redirect to that action :
redirect_to your_controller_action_url
More on : Rails Guide
To just render the new action :
redirect_to your_controller_action_url and return
The logic you present is not MVC, then not Rails, compatible.
A controller renders a view or redirect
A method executes code
From these considerations, I advise you to create methods in your controller and call them from your action.
Example:
def index
get_variable
end
private
def get_variable
#var = Var.all
end
That said you can do exactly the same through different controllers and summon a method from controller A while you are in controller B.
Vocabulary is extremely important that's why I insist much.
You can use url_for to get the URL for a controller and action and then use redirect_to to go to that URL.
redirect_to url_for(:controller => :controller_name, :action => :action_name)
This is bad practice to call another controller action.
You should
duplicate this action in your controller B, or
wrap it as a model method, that will be shared to all controllers, or
you can extend this action in controller A.
My opinion:
First approach is not DRY but it is still better than calling for another action.
Second approach is good and flexible.
Third approach is what I used to do often. So I'll show little example.
def create
#my_obj = MyModel.new(params[:my_model])
if #my_obj.save
redirect_to params[:redirect_to] || some_default_path
end
end
So you can send to this action redirect_to param, which can be any path you want.
Perhaps the logic could be extracted into a helper? helpers are available to all classes and don't transfer control. You could check within it, perhaps for controller name, to see how it was called.
Composition to the rescue!
Given the reason, rather than invoking actions across controllers one should design controllers to seperate shared and custom parts of the code. This will help to avoid both - code duplication and breaking MVC pattern.
Although that can be done in a number of ways, using concerns (composition) is a good practice.
# controllers/a_controller.rb
class AController < ApplicationController
include Createable
private def redirect_url
'one/url'
end
end
# controllers/b_controller.rb
class BController < ApplicationController
include Createable
private def redirect_url
'another/url'
end
end
# controllers/concerns/createable.rb
module Createable
def create
do_usefull_things
redirect_to redirect_url
end
end
Hope that helps.
You can call another action inside a action as follows:
redirect_to action: 'action_name'
class MyController < ApplicationController
def action1
redirect_to action: 'action2'
end
def action2
end
end
Separate these functions from controllers and put them into model file. Then include the model file in your controller.