Double Render Error rails controller - ruby-on-rails

So i'm tyring ot create a custom method in my controller
So far it looks like this
def url
require 'json'
url = url appears here
doc = Nokogiri::HTML(open(url))
doc.css(".ticket-price .h4").each do |t|
json = t.text
respond_to do |format|
format.json { render :json => {:seatname => json}}
end
end
end
However when i run this i get this error
AbstractController::DoubleRenderError
I can't see where the its being called twice? my guess however is that because its inside the each statement. What would be the best way to display the two seat names that come back in json so that it looks like this:
"seatname": "seat1",
"seatname": "seat2"
Thanks
Sam

This should work:
def url
require 'json'
url = url appears here
doc = Nokogiri::HTML(open(url))
// Constructing an array of hash
response_hash = doc.css(".ticket-price .h4").map{ |t| {seatname: t.text} }
respond_to do |format|
format.json { render :json => response_hash }
end
end
Like Max mentioned, in your code respond_to gets called for every matching element. Instead you can construct an array of hash, and then render that.

Give this a shot:
def url
require 'json'
url = url appears here
doc = Nokogiri::HTML(open(url))
response_hash = doc.css(".ticket-price .h4").map { |t| ["seatname", t.text] }.to_h
respond_to do |format|
format.json { render :json => response_hash }
end
end
In your current code, the respond_to block is being called once for each matching element in the return value of doc.css(...), which is causing the DoubleRenderError. To fix that, you need to separate constructing your response data from actually sending it. Does that help?

Related

can I use rabl without using .json in the url?

I have rabl up and running.
I have this in routes:
get 'biblios/collection/:biblio_urn' => 'biblios#biblio_rabl', as: 'collection_biblio'
in the controller:
def biblio_rabl
biblio = Biblio.where(biblio_urn: params[:biblio_urn]).take
end
This url points to the correct result :
http://localhost:3000/dts/biblios/collection/urn:cts:froLit:ed_desmarez:1900
I would like that url to always respond using rabl and showing the the template dts/biblios/biblio_rabl.json.rabl
I mean without adding .json at the end of the url.
I have tried this in the routes.rb, but it doesn't redirect :
get 'biblios/collection/:biblio_urn' => 'biblios#biblio_rabl', as: 'collection_biblio', to: redirect('biblios/collection/%{biblio_urn}.json')
Is that possible at all?
You can force the response to be json by changing the request format in the controller:
request.format = :json
Then make sure you have a respond_to block like this because it's always better to be explicit about your responses:
def biblio_rabl
respond_to do |format|
format.json { json: Biblio.where(biblio_urn: params[:biblio_urn]).take }
end
end

Render different controller methods as JSON in Rails 5.2

I have a resource that renders as JSON perfectly fine at localhost:3000/gins.json from #gins = Gin.order(name: :desc).
Which will return ALL gins. However, I'd like to have a JSON response that only returns the last 4 gins, to use elsewhere. In the controller I also have:
#latestgins = Gin.order("created_at DESC").first(4)
The above would work in an index.html.erb view with <%= #latestgins.name %>, but how do I get the JSON for this? I have tried render json: #latestgins but navigating to localhost:3000/latestings.json, of course, gives a routing error.
I suspect I'm attacking this in completely the wrong way, but only just starting out with Rails API.
you can add respond to format json in your index method:
def index
#gins = Gin.order(name: :desc)
#latestgins = Gin.order("created_at DESC").first(4)
respond_to do |format|
format.html
format.json { render json: #latestgins }
end
end
your #latestgins is now available here : localhost:3000/gins.json
Edit
If you want a custom route to display your data, just add it in your routes:
defaults format: :json do
get 'last4gins', to: "gins#index"
end
Your data for the last 4 entries is available at http://localhost:3000/last4gins.json, at http://localhost:3000/last4gins but also at localhost:3000/gins.json
If you want to keep the gins index route clean, you can also create a custom method and remove the #latestgins from your index:
# routes
get 'last4gins', to: "gins#last4gins"
#controller
def index
#gins = Gin.order(name: :desc)
end
def last4gins
#latestgins = Gin.order("created_at DESC").first(4)
render json: #latestgins
end
Now the data is no more available at /gins.json

Rails 5 best controller action to write a like query

I want to make an ajax search query by a client's name, so I'm using a like clause (see this question). I was thinking of using the index action to respond to json format from clients_controller but I'm already using it to repond to html format and at the same time paginates my listed rows with will_paginate and will_paginate-bootstrap.
What is the best way? making a new method to respond to json format or should I use the index one with format? and How to do that?
I'm new with ruby on rails
clients_controller.rb
def index
respond_to do |format|
format.html
{
#something like this I know that I would return me a syntax error
#client = Client.paginate(:page => params[:page])
}
format.json
{
#something like this I know that I would return me a syntax error
#client = Client.where("client_name LIKE ? ", "%#{params[:client_name]}%" )
}
end
end
def other_method
#client = Client.where("client_name LIKE ? ", "%#{params[:client_name]}%" )
respond_to do |format|
format.json {...}
end
end
In my opinion, you should keep only your index action and just accumulate the scopes on your #client variable.
Remember that your SQL query is only sent to the database when performing an Array method like each on your variable, not before.
So you can write something like:
def index
#client = Client.all
if params[:client_name].present?
#client = #client.where("client_name LIKE ? ", "%#{params[:client_name]}%")
else
#client = #client.paginate(page: params[:page])
end
respond_to do |format|
format.html
format.json
end
end
You should create a new action with some fruitful name like search or search_by_client_name . It will solve your issue also and you will stick with rails restful routing.
If you want index action to serve both request then you can do something like this:
def index
#client = Client.paginate(:page => params[:page])
respond_to do |format|
format.html
format.json do
client = Client.where("client_name LIKE ? ", "%#{params[:client_name]}%"
render json: { client: client }
end
end
end

Wrong number of arguments in as_json [`timeout` method is reserved!]

I get an error in Rails that is related to one of my models, let's call it Unit. The following code produces an error:
format.json { render :json => #units.as_json }
The error is about wrong number of parameters (0 of 1).
I believe what you want is;
def scheme
#object = MonitoringObject.restricted_find params[:id], session[:client]
#units = Unit.where("object_id = ?", #object.id)
respond_to do |format|
format.html
format.json { render :json => #units[0] }
end
end
Or, if you have your relationships set up between the MonitoringObject and Unit models correctly,
def scheme
#object = MonitoringObject.restricted_find params[:id], session[:client]
#units = #object.units
respond_to do |format|
format.html
format.json { render :json => #units[0] }
end
end
You're supplying it with an :except parameter that's empty.
You'll use an :except condition if there are some attributes you don't want included in the JSON response.
According to the rails guide on views and rendering, you don't need to specify .to_json -- it will be called for you automatically.
It looks to me that the problem may lie in your .restricted_find method somewhere. Can you post the entire stack trace )or link to a github gist that contains it?
I solved the problem. From the Rails source:
module Serialization
def serializable_hash(options = nil)
options ||= {}
attribute_names = attributes.keys.sort
if only = options[:only]
attribute_names &= Array.wrap(only).map(&:to_s)
elsif except = options[:except]
attribute_names -= Array.wrap(except).map(&:to_s)
end
hash = {}
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } # exception here
# ...
end
alias :read_attribute_for_serialization :send
# ...
end
# ...
end
So the real error is that one of the methods returned by calling e.g. Unit.first.attributes.keys.sort (["dev_id", "flags", "id", "inote", "ip", "location_id", "model_id", "name", "period", "phone", "port", "snote", "timeout"]) expects me to pass an argument to it. This method is timeout, it is some private method Rails monkey patches Object with. So the correct solution to the problem is to rename this attribute to something else.

Load XML into variable with Rails

This is how I generate XML for purchase model:
# GET /purchases/1
def show
#purchase = Purchase.find(params[:id])
#purchases = Purchase.all
respond_to do |format|
format.html # show.html.erb
format.xml { render :action => "something.xml.builder", :layout => false }
end
end
Now I'd like to get this rendered XML as string into variable so I could post it to WebService.
How can I get XML through sales_invoice.xml.builder without rendering it?
I don't want use dirty hacks and loading XML from http://appurl/purchases/1.xml
Thanks!
What I was looking for was render_to_string method.

Resources