Rails : render and exit immediately - ruby-on-rails

Using the omniauth gem, I am forced to define a single route callback for succesful logins, regardless of the provider :
def auth_callback
auth_data = request.env['omniauth.auth']
if auth_data.has_key('something')
process_one(auth_data)
else
process_two(auth_data)
end
# No view is available here
end
def process_one
# do something then render view for process_one
return
end
def process_two
# do something then render view for process_two
return
end
How can I prevent the controller from returning to the auth_callback method and try to display the corresponding view (which does not exist) ? Treatment should be considered as complete once the process_one or process_two methods have returned.

Why not specifically call render in those methods?
def process_one
# do something then render view for process_one
render :process_one and return
end
Rails should detect that you've already run it and not try to render again.

If you want to return from the chain of methods, e.g.
def a
...
b
...
render "smth"
end
...
def b
...
# render from some conditional from here
...
end
will cause AbstractController::DoubleRenderError, which means that you call render twice.
You can read this article to find out 4 ways to manage such situation.

Related

active admin how to render form on index page if object not created

I can't understand what I do wrong. My code won't work if subscription blank, but if I created it from rails c, all works fine...
# frozen_string_literal: true
ActiveAdmin.register Subscription do
actions :index
index do
result = Subscriptions::GetPricing.call(admin: current_admin)
if result.success?
render partial: 'subscription_form', locals: { amount: result.plan.amount }
else
flash[:alert] = result.message
render partial: 'subscription_errors'
end
end
end
Now i get: There are no Subscriptions yet. simple message.
And i want left all styles, nav panel, etc how it default, but in container should be store my code from partials.
If your intent is to display an input form if the table is unpopulated then try this:
controller do
def index
collection.size == 0 ? redirect_to(new_subscription_path) : super
end
end
I think what you are trying to do is a bit wrong. index do block is to render the view level mainly. If you want to override controller action you will have to do it like below -
controller do
def index
# your code here
end
end
Have a look at the documentation -
https://activeadmin.info/8-custom-actions.html
https://activeadmin.info/3-index-pages.html

Ruby/Rails break out of nested method

I have a method1 which calls other methods depending on params and then returns a json. One of these methods checks if a specific user exists. If the user doesn't exist the method should render a JavaScript alert. At first I got an error that render was called multiple times (which was correct). So I tried adding break but received an invalid break error. So I tried return but then I still get the Render and/or redirect were called multiple times in this action. How can I break out of method1 when I'm in method2, so that only the render in method2 gets called?
def method1
data = case params["foobar"]
when "case1"
methodxy
...
else
method2
end
render json: data
end
def method2
if user.exists?
return {...}
else
render(
html: "<script>alert('Unknown user!')</script>".html_safe,
layout: 'application'
)
return
end
end
Technically you could achieve this with a throw ... catch, which is essentially acting like a GOTO statement. But I would not advise this; the code is already too messy, and you'd be making the problem worse.
Instead, you should aim to clean up the flow in method1. Make the logical flow and responses clearer. For example, maybe something like:
def method1
if !user.exists?
render(
html: "<script>alert('Unknown user!')</script>".html_safe,
layout: 'application'
)
else
data = case params["foobar"]
when "case1"
methodxy
...
else
method2
end
render json: data
end
end
def method2
# ...
end
You could then refactor this further, e.g. by moving the user.exists? check into a before_filter and moving that case statement into its own method, to simplify things.
The end result could look something along the lines of:
before_filter :ensure_user_exists, only: :method1
def method1
render json: foobar_data
end

Rails way to render different actions & views based on user type?

I have a couple different user types (buyers, sellers, admins).
I'd like them all to have the same account_path URL, but to use a different action and view.
I'm trying something like this...
class AccountsController < ApplicationController
before_filter :render_by_user, :only => [:show]
def show
# see *_show below
end
def admin_show
...
end
def buyer_show
...
end
def client_show
...
end
end
This is how I defined render_by_user in ApplicationController...
def render_by_user
action = "#{current_user.class.to_s.downcase}_#{action_name}"
if self.respond_to?(action)
instance_variable_set("##{current_user.class.to_s.downcase}", current_user) # e.g. set #model to current_user
self.send(action)
else
flash[:error] ||= "You're not authorized to do that."
redirect_to root_path
end
end
It calls the correct *_show method in the controller. But still tries to render "show.html.erb" and doesn't look for the correct template I have in there named "admin_show.html.erb" "buyer_show.html.erb" etc.
I know I can just manually call render "admin_show" in each action but I thought there might be a cleaner way to do this all in the before filter.
Or has anyone else seen a plugin or more elegant way to break up actions & views by user type? Thanks!
Btw, I'm using Rails 3 (in case it makes a difference).
Depending on how different the view templates are, it might be beneficial to move some of this logic into the show template instead and do the switching there:
<% if current_user.is_a? Admin %>
<h1> Show Admin Stuff! </h1>
<% end %>
But to answer your question, you need to specify which template to render. This should work if you set up your controller's #action_name. You could do this in your render_by_user method instead of using a local action variable:
def render_by_user
self.action_name = "#{current_user.class.to_s.downcase}_#{self.action_name}"
if self.respond_to?(self.action_name)
instance_variable_set("##{current_user.class.to_s.downcase}", current_user) # e.g. set #model to current_user
self.send(self.action_name)
else
flash[:error] ||= "You're not authorized to do that."
redirect_to root_path
end
end

Conditional root_url (index)

I'd like my application to display different data on the frontpage depending on whether the user has been logged in or not.
def index
if current_user
# render another controllers action
else
# render another controllers action
end
end
I can achieve this by using render_component. However it has been obsolete for some time. Although I can still use it as a plugin, I'm interested if anyone has a better approach.
Just take in mind that rendering another controller's view directly is not an option.
Thanks.
Just use your index method as a public proxy to the specific view you want to render.
def index
if user?
logged_in
else
logged_out
end
end
private
def logged_in
# stuff
render :action => "logged_in"
end
def logged_out
# stuff
render :action => "logged_out"
end
If it is a relatively small subsection of data, I'd probably do that in a view helper.

How can I determine whether a controller action has rendered (rather than redirected?)

The performed? method returns true in a controller if the application has redirected or rendered.
How can I determine whether the application rendered (rather than redirected)?
Judging from the performed? docs, I can look at #performed_render, but this seems hacky.
Edit: Why do I want to do this?
I'm trying to cache the output of a controller action only in the case that it results in a render (and not a redirect). Here's the code I'm using now: (based on render-caching)
def render_with_cache(options = {})
key = ([request.request_uri, request.format] | (options.delete(:key_additions) || [])).join("::")
body = Rails.cache.read(key)
if body
render :text => body
else
yield if block_given?
render unless performed?
if #performed_render
Rails.cache.write(key, response.body, options) # :expires_in
end
end
end
Rails 3.2+ now uses a method called performed? for determining whether or not a render or redirect has already been performed.
Contrived example:
def index
redirect_to not_found_path unless authenticated?
render action: "show_all" unless performed?
end
Look at #performed_render. :) You don't explain why you need to detect this, so I am unable to suggest alternative solutions.
In an after filter, check the codes for the response status .
MyController < ActionController
after_filter :check_response
# define your actions that render or redirect
protected
def check_response
# put real app logic here
puts :rendered! if response.status == "200 OK"
puts :redirected! if response.status == "302 Found"
end
end

Resources