responding with multiple JSON renders. (Ruby/Rails) - ruby-on-rails

This is a relatively simple one and I'm pretty sure its just syntax.
Im trying to render multiple objects as json as a response in a controller. So something like this:
def info
#allWebsites = Website.all
#allPages = Page.all
#allElementTypes = ElementType.all
#allElementData = ElementData.all
respond_to do |format|
format.json{render :json => #allWebsites}
format.json{render :json =>#allPages}
format.json{render :json =>#allElementTypes}
format.json{render :json =>#allElementData}
end
end
end
Problem is I'm only getting a single json back and its always the top one. Is there any way to render multiple objects this way?
Or should I create a new object made up of other objects.to_json?

you could actually do it like so:
format.json {
render :json => {
:websites => #allWebsites,
:pages => #allPages,
:element_types => #AllElementTypes,
:element_data => #AllElementData
}
}
in case you use jquery you will need to do something like:
data = $.parseJSON( xhr.responseText );
data.websites #=> #allWebsites data from your controller
data.pages #=> #allPages data from your controller
and so on
EDIT:
answering your question, you don't necessarily have to parse the response, it's just what I usually do. There's a number of functions that do it for you right away, for example:
$.getJSON('/info', function(data) {
var websites = data.websites,
pages = data.pages,
...
});

Related

Rendering Two JSON Items In Rails

This seems like a duplicate question but the answers on the others posts don't seem to work for my issue here.
I'm needing to render two JSON items here within my index method in my controller:
def index
#user = User.all
#libraries = Library.all.order(:created_at)
user_cards = current_user.libraries
render :json => #libraries, :include => :user
render :json => user_cards
end
I attempted to do it this way (failed with a 500 error):
render :json => #libraries, user_cards, :include => :user
And I also attempted to do it this way (also failed with a 500 error): render :json => #libraries :include => [:user, :user_cards]
UPDATE
This is the most recent attempt as rendering the json properly:
def index
#user = User.all
#libraries = Library.all.order(:created_at)
user_cards = current_user.libraries
render json: {
user_cards: user_cards,
libraries: #libraries.as_json(include: [:user])
}
end
The issue with this is that I am now getting an error on libraries throughout my application as it stands. If I simply just render json like I originally had it (render :json => #libraries, :include => :user), I do not get this error. So, I'm assuming the way I have it is still not correct. The exact error on libraries is being called within one of my React components where I use filter:
Error: Uncaught TypeError: this.props.librarys.filter is not a function
Error Location:
let filteredCards = this.props.librarys.filter(
(library) => {
return library.title.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1 || library.desc.toLowerCase().indexOf(this.state.search.toLowerCase()) !== -1
}
)
Controller can only return one response, you can achieve this with combining this two returns into one:
respond_to do |format|
format.json { render json: { user_cards: user_cards,
libraries: #libraries } }
end

OLA API integration in Ruby on Rails web API {"code":"invalid_partner_key","message":"Partner key is not authorized"}

I am passing the basic URL required to call OLA API with x app token in HTTParty, but it gives this error:
{"code":"invalid_partner_key","message":"Partner key is not authorized"}
Here is the code:
require 'rubygems'
require 'httparty'
class ProductsController < ApplicationController
def index
lat = params[:lat].to_s
long = params[:long].to_s
#results = HTTParty.get("https://api.uber.com/v1/products?server_token=my_token&latitude="+lat+"&longitude="+long).parsed_response
#result1 = HTTParty.get("https://devapi.olacabs.com/v1/products?X-APP-TOKEN=my_token&pickup_lat=12.9491416&pickup_lng=77.64298").parsed_response
respond_to do |format|
format.json { render :json => JSON.parse(#results) }
format.json { render :json => JSON.parse(#result1) }
format.html { render "index.html.erb" }
end
end
end
Why is this happening, and how do I fix it?
Going through the olacabs docs, your X-APP-TOKEN should be passed as a header and not along with the payload. So you should be doing something like this
query = {
"pickup_lat" => 12.9491416,
"pickup_lng" => 77.64298
}
headers = {
"X-APP-TOKEN" => your_token
}
#result1 = HTTParty.get(
"https://devapi.olacabs.com/v1/products",
:query => query,
:headers => headers
).parsed_response
I didn't test this out but should give a good start to figuring your issue out.

Two different as_json methods

I'd like to have my json render in two different ways. I now have my as_json method overridden to display a full object in json form like this:
{
prop1: stuff,
prop2: stuff,
innerthings: {
{
prop1:stuff,
prop2:stuff
}
{
prop1:stuff,
prop2:stuff
}
}
}
And the as_json looks like:
#Renders json
def as_json(options={})
super(
:except => [:created_at, :updated_at],
:include => [{:innerthings = > {
:except => [:created_at, :updated_at]
}}]
)
end
I'd also like to have a second option to render like this:
{
prop1:stuff,
prop2:stuff,
countinnerthings:10
}
current when the code below is used, I get the first render:
respond_to do |format|
format.json { render json: #thing}
end
I'd also like to be able to render with something like as_list that I could use in a case like the below to render just a simple list of the objects.
respond_to do |format|
format.json { render json: #things.as_list }
end
Is there a simple way to do this in ruby on rails?
Instead of rolling your own as_json method. Take a look at ActiveModel Serializers. I think it will satisfy you use-case and help organize your code.
You can just define an as_list method
def as_list
{
prop1: "stuff"
}
end
If you need to use includes etc you can call as_json from your at_list method. As had been said serializers are a better option in general.

How to access the updated object in a javascript callback (instead of the old object)

Building on the helpful and working solution presented here, I'm trying to fix my update callback as well.
Problem is, the specific unit that I'm trying to extract data from is always the old cached version, even though this callback is triggered by a successful update action.
// callback triggered by the update action
$('.best_in_place').bind("ajax:success", function () {
...
console.log(unit.duration);
// which is exactly the same as
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
// and both print the OLD duration val instead of the updated val which is in the database
});
and the unit_users_controller code...
def update
#unit = #unituser.unit
respond_to do |format|
if #unituser.update(unit_user_params)
#unit.reload
logger.info('-----------------------------------------------------------------')
logger.info('#unit.duration in the controller is ' + #unit.duration.to_s) # which is the correct value
logger.info('-----------------------------------------------------------------')
gon.unit_duration = #unit.duration # an experiment which didn't work for me
format.json {respond_with_bip(#unituser) }
else
# format.html { render :action => 'edit' }
format.json { respond_with_bip(#unituser) }
end
end
end
I've tried several versions of unit.reload, and nothing helps. Maybe I was putting it in the wrong place?
I did this one sometime ago here is my code, maybe it will help you:
Javascript:
$(document).ready(function() {
$('.price_bind').bind("ajax:success", function (event, data, status, xhr) {
var parsed_data = jQuery.parseJSON(data);
$(this).text(parsed_data.newprice);
$(this).parentsUntil('body').find(".totalpricep span").text(parsed_data.totalprice);
});
}
View:
<%= best_in_place detail, :price, :classes => 'price_bind', :path => purchase_detail_path(#purchase, detail)%>
Controller:
def update
respond_to do |format|
if #detail.update_attributes(params[:detail])
#n=#detail.mk_bal
#r=false
if #detail.purchase != nil
#p=#detail.purchase.totalprice
if params[:detail]['status'] && #purchase.step==1
#remdet = #purchase.details.where(:step => 1, :status => false)
if #remdet.empty?
#purchase.update_attribute(:step, 2)
#r=true
end
end
else
#p=nil
end
format.html { redirect_to #detail, notice: 'Detail was successfully updated.' }
format.json { render :json => {:newprice => #n, :totalprice => #p, :newstatus => #detail.status, :refresh => #r}}
else
format.html { render action: "edit" }
format.json { render json: #detail.errors, status: :unprocessable_entity }
end
end
end
This isn't about caching. Your Ruby code is evaluated server-side, before the JavaScript is ever send to the client, and it's only evaluated once, long before the AJAX request can happen.
The client never sees this line:
console.log(<%= Unit.find(unit.id).unit_users.pluck(:duration).sum %>);
All the client will see is something like:
console.log(32); // or whatever the sum is
You cannot use <%= %> here. That will always give you the original value. Instead, you need to send the new value to the client in response to the AJAX request.

How can I include a model association in a JSON response in Rails?

I've looked at similar posts but can't seem to quite figure it out.
I have the following function which works just fine. The Listing model has a foreign key called price_id which maps to the Price model and its price_range column. Price_id is returned as part of the message object in the JSON response.
How can I return the corresponding price_range value from the association instead of the price_id value (as part of the message obj, and keep the other attributes)?
def update
#listing = Listing.find(params[:listing][:id])
#if params were passed in for updating
if #listing.update_attributes(params[:listing])
#should we return the whole thing or just what's needed?
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.attributes #USE attributes to show output the content of the #message obj, and not another object called "message"
}
respond_to do |format|
#json response
format.html { render:json => json_response }
format.xml { render :xml => #listing }
#normal response. Consider leaving this for now?
#format.html { render :action => "detail" } #refresh this page, with new data in it. Consider trying to use redirect instead?
#format.xml { head :ok }
end
end #end if
end
add a method in your Listing model with the price_range and call it in serializable_hash
class Listing
def price_range
price.price_range
end
end
Like explain on comment you can use delegate instead this method :
class Listing
delegate :prince_range, :to => price
end
In you controller you can now do :
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.serializable_hash(:methods => [:price_range])
}
Based on what I read in this article, you should be able to do this:
class Listing
def as_json
super(:include => :price)
end
end
Then in your controller:
json_response = {
"success" => #listing.save,
"message" => #listing.as_json
}
If I understand correctly, you want to add #listing.price.price_range value to the "message" ?
If so, try this:
"message" => #listing.attributes[:price_range] = #listing.price.price_range

Resources