Issues rendering text into json response in rails controlelr - ruby-on-rails

I'm trying to return json from my method in my app
Heres what i have
doc.css(".secondary-text").each do |t|
price1 = t.text
respond_to do |format|
format.json { render :json => {:testing => price1}}
end
end
However i'm getting DoubleRenderError. Whats returned is this:
(£28.50 Ticket + £4.00 Fees)
(£35.00 Ticket + £5.25 Fees)
(£22.50 Ticket + £3.50 Fees)
(£35.00 Ticket + £5.25 Fees)
(£22.50 Ticket + £3.50 Fees)
Is it because its inside the each method?
Thanks

Yes, it's because you use render as many times as many objects are present in doc.css(".secondary-text"). Render can be used only once per scope.
If you want to render whole collection, you should do something like that:
collection = doc.css(".secondary-text").each.inject([]) do |collection, t|
collection << t.text
collection
end
respond_to do |format|
format.json { render json: { collection: collection } }
end

Related

Rails:Receive POST with JSON array

I'm currently using a controller to receive POST with one json object at a time. And I want it change to receiving the whole array. How can I modify my controller?
Current Controller
def create
respond_to do |format|
#targetrecord = TargetRecord.new(targetrecord_params)
#targetrecord.save
if #targetrecord.save
format.json{ render :json => #targetrecord.to_json ,status: 200 }
else
format.json { render json: #targetrecord.errors, status: 404 }
end
end
end
end
def targetrecord_params
params.require(:targetrecord).permit(:id, :uuid, :manor, :mac, :beacon_type, :longitude, :latitude, :address, :findTime, :rssi, :finderID, :created_at, :updated_at )
end
I'm sending the POST as below right now
"targetrecord":
{"id":"","name":"",.....}
And I want to send multiple sets as an array like
"targetrecord":[
{"id":"1","name":"",.....},
{"id":"2","name":"",.....},
....]
How can I let my controller know that she needs to extract and create one by one? Thanks a lot!
If you are POSTing an array, then the array will just be part of your params object when processed by the controller action. So you should be able to loop through the array and create an array of TargetRecord objects. You'll need to modify your targetrecord_params method to allow it to accept an argument since you can't just look at 'params' in that context once you make the change. You'll also need to find a way to track whether or not all the records have saved successfully.
I haven't tested this code, but something like this should get you going in the right direction, I think:
def create
respond_to do |format|
#targetrecords = []
save_succeeded = true
params[:targetrecord].each do |record|
tr = TargetRecord.new(targetrecord_params(record))
save_succeeded = false unless tr.save
targetrecords << tr
end
if save_succeeded
format.json{ render :json => #targetrecord.to_json ,status: 200 }
else
format.json { render json: #targetrecord.errors, status: 404 }
end
end
end
end
def targetrecord_params(record)
record.require(:targetrecord).permit(:id, :uuid, :manor, :mac, :beacon_type, :longitude, :latitude, :address, :findTime, :rssi, :finderID, :created_at, :updated_at )
end

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.

Can I save data in more that one time for single request in rails?

There is model call Event which contains following attributes
start_at,
end_at,
details,
trainer
This is my normal create method which generated my scaffold command
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
format.html { redirect_to(#event, :notice => 'Event was successfully created.') }
format.xml { render :xml => #event, :status => :created, :location => #event }
else
format.html { render :action => "new" }
format.xml { render :xml => #event.errors, :status => :unprocessable_entity }
end
end
end
and I need to modify this as follows when particular event object have date gap between stat_at and end_at more than 6 I need to save that event as two events. First event will started at original started date and end date should be middle date of the event and other data are same. But in second event it should be start date as middle date and end date should have original end date. Can some one explain how could I done this???
After the line if #event.save you can create another Event record like this
Please note this code is untested.
if (#event.end_at - #event.start_at) > 6
event2 = Event.new
event2.start_at = something
event2.end_at = anotherthing
event2.save
end
As you are going to do some logic, you can write a method to perform your logic and call that method from controller. Better keep the method in Event model to satisfy good practice of Fat Model and Lean Controller.
def self.create_event(ev_params)
status = false
if event.end_dat - event.stat_dat >6
# create two events
event1 = Event.new(ev_params)
mid_date = event1.stat_at + ((event.end_dat - event.stat_dat)/2).days
event1.end_at = mid_date
event2 = Event.new(ev_params)
event2.stat_at = mid_date
status = event1.save'
status = event=2.save'
else
status = Event.create(ev_params)
end
status
end
And call this method from controller Event.create_event in controller #event.save.
This is not tested code but I hope you can easily get from above code.

responding with multiple JSON renders. (Ruby/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,
...
});

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