Render and return from helper function - ruby-on-rails

I have multiple actions in different controllers making 3rd party API calls through Faraday using a helper function defined in application_helper.
Now in case of a successful response, they have different views to render, but need to send json which was recieved if there was any error.
The following code works when used directly in an action,
if (r[:error] && r[:code]==403)
Rails.logger.info "Exiting"
render :json => body and return
end
But if I move it to a helper function and call the helper function from the action, the execution doesn't stop there and continues till the end of the action where it raises DoubleRendererError.
Could anything be done to make the controller stop processing and return itself from the helper (to avoid placing these 4 lines in every action, where I make the 3rd party API call)

def blank_fields?(params)
if params["user"]["email"].blank? || params["user"]["password"].blank? || params["user"]["password_confirmation"].blank?
render json: { status: 422, message: "Your email, password or password confimation is blank. Please check that all fields are filled in and try again!" }, status: 422
return true # pass back to guard clause
end
end
do something like this, and call this method from your controller method, aka:
return if blank_fields?(params)

Rails' helpers are intended for calling from views, and they way you're using render is to be called from a controller. You can call helpers from within a controller but it's generally not necessary. If you want some function to be available to all controllers, you would typically place it in your ApplicationController.
When you call render within a view, it is going to try to render a partial at the point in the template you called it.

Related

API Controller Specifically for shared methods

Would it be best practice to create some sort of 'general api controller' for shared methods among different models in my RoR application? For example I'm including email validation for Customers - once the user finishes typing in the email address (before submit), an $.ajax POST request is sent to the customers_controller where an external API is being called. After parsing the response I return a response back depending on certain conditions. The validation is handled in real time on the front end before submitting so that the user can know if an email address is valid or not before submitting. The controller method is below.
def validate_email_address
response = ValidateEmailAddressJob.perform_now(params[:email], "Agentapp")
data = JSON.parse response.body
if data['result']['verdict'] == "Valid" || data['result']['verdict'] == "Risky"
html = "<div class='text-success'><i class='fa fa-check'></i> This email appears to be valid</div>"
render json: {html: html, result: data['result']}, status: 200
elsif data['result']['verdict'] == "Invalid"
html = "<div class='text-warning'><i class='fa fa-exclamation'></i> This email may be invalid</div>"
render json: {html: html, result: data['result']}, status: 200
else
render json: {}, status: 503
end
end
I'm trying to keep my code as DRY as possible, so if I wanted to share this email_validation controller method with VendorContacts for example, I'm thinking I could just point the other VendorContact API call to /customers/validate_email_address endpoint even if it's not technically a customer that's needing the validation? Or I could just copy and paste the controller method into the vendor_contacts_controller? Or I could create a separate general api controller with an endpoint such as /validation/emailaddress.
Thanks ahead of time for any help, I'm just trying to learn what the appropriate way to handle this situation would be.
I don't know if this would be considered "best practice" but as a long-time Rails dev, my approach would be to create an EmailController controller, with a single action create, and an Email model (not inheriting from ActiveRecord::Base).
class EmailController < ApplicationController
def create
email = Email.new(params[:email])
if email.valid?
render partial: :valid_email_response
else
render partial: :invalid_email_response
end
end
end
So the Email model has an initialize method, and a valid? method, and the html response is contained in separate view files. Of course, nothing is saved to the database here. The EmailController#create action is invoked any time you wish to validate an email address.
My rationale is that controllers should implement CRUD methods, and in this case a model should handle the validation. I believe this is entirely consistent with the "Rails Way".
IMHO Rails controllers can become catch-all classes for all kinds of application logic, and that coercing the logic into MVC format makes the cleanest code.

DoubleRenderError with no_content return and no other code

I have the following function in my controller:
api! 'Destroy a book'
def destroy
head :no_content
return
....
end
But everytime I call my method I get a double render error!
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".
What could possibly be causing this? I have a before_destroy on my book model defined, but if Im correct that's a) not being called and b) even if it was, still couldnt cause a double render.
It's worth noting that if I reverse the order of the two lines I get a "no template" error, which I believe is what happens if you don't specify a render or redirect.
The problem was in the class that my controller inherited from. The controller never specified an authorization so it was trying to render the login page. Thats why the controller gave a double render error.

Returning to coffee script calling a controller method, without rendering a view

Rails 3.2.18
Ruby 2.1.5
Ubuntu 14.04
In my routes.rb, I have:
post '/tracker' => 'loggers#create'
In my loggers_controller.rb, I have:
class LoggersController < ApplicationController
def create
logger.info(params[:data])
end
end
In the coffee script file I am calling this from, I have:
$.post '/tracker', { data: "some text"}
When the code runs, I am getting an error message:
ActionView::MissingTemplate (Missing template loggers/create
I understand why I am getting the message, but what do I need to do to make sure that control is returned to the coffee scriptthat called the controller method, so the next statement in the coffee script is executed?
What I am looking for essentially is a way to log something in the log file from the coffee script.
The problem is that the controller action will have to return a status for your request, even if you do not want any content. The easiest way to do that is to use head :no_content in your action, right after the logger.info call. This will return a success status (204), but won't try to render any template. You could also use head :ok, but no_content is clearer as you are actually not sending back any content. So:
def create
logger.info(params[:data])
head :no_content
end
Please also note that technically your javascript never loses control, as the request is asynchronous and the script will immediately continue its execution unless you specify a callback.

How to ask two queries from a single controller

I am trying to do everything on a single page. The page is called users/index.html.erb.
On this page I return a simple data base query in json format.
I would like to also return a seperate query in non json format from the same database.
I'm trying to avoid partials for the moment as they make variable handling a bit more complicated that I want for my beginner level.
This is what I have:
class UsersController < ApplicationController
def index
#users = User.including_relationships
#followas= User.find_by name: params[:name]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
end
but it doesnt work unsurprisingly. How can I get it to work in one action?
Not sure about your purpose of returning two formats of query from a single controller call. It usually happens when you use Javascript heavily (eg. Angular.js). Controller first returns the HTML page including all the HTML template and javascript, then data in JSON is returned in the second round.
So in your users/index.html.erb, you might have something like:
<p ng-repeat='user in users'>{{user.name}}</p>
<script>
// do a AJAX call to retrieve users in JSON format
</script>
Then you will call users/index.json again to retrieve users information in JSON.
In short, your users/index is called twice, once for html, once for JSON.
It might seem unwise to call the same controller method twice just to render different format. In such case, you might consider to embed the JSON data in HTML. In such way, you will only render users/index.html.erb once and parse the data in JSON through Javascript on the client side.

Passing json from index action and rendering of another application layout simultaneously. Ruby on Rails

I have Vacancies controller and I need to pass #vacancies to json and also render another layout. The following code does not work (json is not passed however I have "wide" layout). If I remove format.html { render layout: "wide"} } json passes correctly. How to combine these two things?
class VacanciesController < ApplicationController
respond_to :html, :json
...
def index
#vacancies = Vacancy.all
respond_with(#vacancies) do |format|
format.html { render layout: "wide"} }
format.json { render json: #vacancies }
end
end
...
You can't call render twice, that's problem #1. You also can't send two responses to a single request.
There is also no purpose in rendering HTML (which means a fresh page load) and sending JSON (which is for AJAX requests, ie requests that don't reload the page) at the same time. It isn't possible, but it also would be pointless even if it was possible.
If you want to tell a request to use a specific layout, you can pass the layout option to a render call. However, a render call does not take a data object as the first argument, it takes a view name, or only an options hash. So to call this correctly you should use:
render :index, :layout => 'example'
I expect that will make your HTML views show up correctly.
Please understand however, the layout option is only useful for HTML responses, not JSON responses. Layout is telling your render call what outer HTML to wrap around the view your action is calling, and if you don't specify it uses 'application.html'
To help you understand one more thing: your respond block is telling the computer how to respond to different kinds of requests. It's like a switchboard. If you wrote it with if/else statements it might look like this:
if request_type == 'html'
render :index, :layout => 'wide'
elsif request_type == 'json'
render :json => #vacancies
else
raise raise ActionController::UnknownFormat
end
So, with your respond_with block, if you fix your html render call, and assuming you're developing on localhost, if you enter the following URL in your browser and hit enter...
http://localhost:3000/vacancies
That would be making an HTML format GET request, which will load the page with layout: 'wide' but no other data. If you type:
http://localhost:3000/vacancies.json
That will simulate a JSON request, and you'll get just the JSON representation of the #vacancies data.
I hope that helps you solve your problem. If not, please describe what you're trying to accomplish in more detail so I can help you understand how to do it.
PS: one last tip: if you want to specify layouts at the controller level you can just call layout at the top of your controller, like so:
class ExampleController < ApplicationController
layout 'awesome', :only => [:new,:edit]
...
end
This works like any other filter, you can pass :only, or :except, or no options at all.

Resources