Preparing Rails JSON for Ember Data - ruby-on-rails

Ember-data seems to want an array of data for a hypothetical Color model to look like:
{
"colors": [{
name: "red"
}, {
name: "blue"
}, {
name: "green"
}]
}
i.e. It wants a root element that is the plural of whatever type your model is. I am wondering how to get rails to send JSON that way using the active_model_serializers gem. Here is what I have:
# GET /colors
# GET /colors.json
def index
#colors = Color.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #colors, :root => 'colors' }
end
end
but this produces:
{
colors: [
{
colors: {
name: "red"
}
}, {
colors: {
name: "blue"
}
}, {
colors: {
name: "green"
}
}
}]
}
i.e. There is a root element on the array and each object. I want it just on the array. Any help is appreciated. Thanks!

I eventually solved this by adding my own custom serializer
app/serializers/color_serializer.rb
class ColorSerializer < ActiveModel::Serializer
attributes :name
self.root = false
end
and then used
render json: #colors, each_serializer: ColorSerializer
which disabled the per-object root element while preserving the root element on the array.

Related

Build array from two hashes

I try to build json with geojson datas.
In my controller :
def index
....
respond_to do |format|
format.html
format.json { render json: { type: 'FeatureCollection', features: pois_geojson + tracks_geojson} }
end
and for show
def show
...
respond_to do |format|
format.html
format.json { render json: { type: 'FeatureCollection', features: poi_geojson + track_geojson} }
end
For index, all work fine and my json is good. I call this method for build json.
Methods for show
def poi_geojson
{
type: 'Feature',
RGeo::GeoJSON.encode(#poi.lonlat),
properties: {
name: #poi.name,
:'marker-color' => '#00607d',
:'marker-symbol' => 'circle',
:'marker-size' => 'medium'
}
}
end
def track_geojson
{
type: 'Feature',
geometry: RGeo::GeoJSON.encode(#track.path),
properties: {
:'color' => '#ff7800',
:'weight' => '5',
:'opacity' => '0.65'
}
}
end
Methods for index
def pois_geojson
#pois.map do |poi|
{
type: 'Feature',
RGeo::GeoJSON.encode(poi.lonlat)
properties: {
name: poi.name,
:'marker-color' => '#00607d',
:'marker-symbol' => 'circle',
:'marker-size' => 'medium'
}
}
end
end
def tracks_geojson
#tracks.map do |track|
{
type: 'Feature',
geometry: RGeo::GeoJSON.encode(track.path),
properties: {
:'color' => '#ff7800',
:'weight' => '5',
:'opacity' => '0.65'
}
}
end
end
As you can see, the methods are similars, but I don't understand why for index it work fine, and not for the show.
I have this error :
`undefined method '+' for #`
for this line :
`format.json { render json: { type: 'FeatureCollection', features: poi_geojson + track_geojson} }`
There is no + method for hash instances, to form an array from two hashes you can do the following:
[pois_geojson, tracks_geojson]
The reason why this works for pois_geojson and tracks_geojson is because both of them are already arrays.

how to create a custom json from raw data (database data)

I have create a show method in tour controller and want to render the data from the database into json format.
This the definition of the tour controller
class ToursController < ApplicationController
respond_to :json
def show
#tourcategory = Tourcategory.find(params[:id])
#tours= #tourcategory.tours
#tourcategories = Tourcategory.all
render :layout => false
end
end
This is the definition of the show.html.haml view
%h3 Tour for #{#tourcategory.title}
= #tours.to_json
The output of this code is following:
[{"content":"dscfds","created_at":"2015-12-12T09:48:32Z","elementid":"test1","id":8,"jobid":2,"next_button_title":"next","priority":23,"title":"test1","updated_at":"2015-12-12T09:48:32Z"}]
But i just want to render the data in this kind of json format, following:
var tour = {
id: "tour",
steps: [
{
title: "abc",
content: "Click this Button.",
target: "#abc",
placement: "bottom",
showNextButton: false,
skipIfNoElement : true
},
It's not clear what you're trying to achieve but it seems to me that you want to extract certain attributes from your #tours and group them as an array, if that's the case you can do something like this:
(list all the attributes you want inside t.attributes.slice())
tour = { id: "tour", steps: #tours.map { |t| t.attributes.slice("title", "content", "target") } }
and if you want to convert your keys from snake (underscore) to camel format:
tour = {
id: "tour",
steps: #tours.map {|t| t.attributes.slice("title", "content").map {|k,v| [k.camelize(:lower), v]}.to_h}
}

Rails render json object with camelCase

I have the following controller code in a simple Rails API:
class Api::V1::AccountsController < ApplicationController
def index
render json: Account.all
end
def show
begin
render json: Account.includes(:cash_flows).find(params[:id]), include: :cash_flows
rescue ActiveRecord::RecordNotFound => e
head :not_found
end
end
end
The problem with this is that, the generated json have the format:
{
id:2,
name: 'Simple account',
cash_flows: [
{
id: 1,
amount: 34.3,
description: 'simple description'
},
{
id: 2,
amount: 1.12,
description: 'other description'
}
]
}
I need that my generated json is camelCase('cashFlows' instead of 'cash_flows')
Thanks in advance!!!
Following the recommended by #TomHert, I used JBuilder and the available config:
Keys can be auto formatted using key_format!, this can be used to convert keynames from the standard ruby_format to camelCase:
json.key_format! camelize: :lower
json.first_name 'David'
# => { "firstName": "David" }
You can set this globally with the class method key_format (from inside your environment.rb for example):
Jbuilder.key_format camelize: :lower
Thanks!!!

Sencha Touch RestProxy with Rails example needed

I've got the following model
Ext.regModel("Entries", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "bought", type: "boolean"},
],
proxy: {
type: 'rest',
url: '/lists',
format: 'json',
reader: {
type: 'json'
}
}
});
Then I've got a list, which is populated from this model
...
store: new Ext.data.Store({
model: "Entries",
autoLoad: true,
remoteFilter: true
}),
...
The list is populated correctly. But when I try to perform the following
listeners: {
itemswipe: function (record, index, item, e) {
var el = Ext.get(item);
el.toggleCls("crossedOut");
var store = record.getStore();
var rec = store.getAt(index);
if (el.hasCls('crossedOut')) {
rec.set('bought', true);
rec.save({
success: function() {
console.log("Saved!");
}
});
} else {
console.log('not crossed out');
rec.set('bought', false);
rec.save({
success: function() {
console.log("Saved!");
}
});
}
}
}
when swipe event is fired, I've got the following error
Uncaught TypeError: Cannot read property 'data' of undefined gsencha-touch.js:6
(anonymous function) sencha-touch.js:6
Ext.data.Connection.Ext.extend.onCompletesencha-touch.js:6
Ext.data.Connection.Ext.extend.onStateChangesencha-touch.js:6
(anonymous function)
I understand, that there is some problem with the payload I return, but I can't find an example of the correct one, and all my guesses do not work.
In backend I return the following
format.json {render :json => {:data => #list, :success=> true, :id => #list.id}}
I'm using the ExtJS 4 preview, but it should work the same with Sencha Touch. Your problem might be related to the nesting of the returned json. Here's what works for me.
In the Rails controller:
def index
respond_with #entries = Entry.all do |format|
format.json { render :json => {:success => true, :data => #entries, :total => #entries.count} }
end
end
def show
respond_with #entry = Entry.find(params[:id]) do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
def create
respond_with #entry = Entry.create(params[:records][0]) do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
def update
#entry = Entry.find(params[:id])
#entry.update_attributes(params[:records][0])
respond_with #entry do |format|
format.json { render :json => {:success => true, :data => [#entry]} }
end
end
ExtJS model:
Ext.regModel("Entries", {
fields: [
{name: "id", type: "int"},
{name: "title", type: "string"},
{name: "bought", type: "boolean"},
],
proxy: {
type: 'rest',
url: '/lists',
format: 'json',
reader: {
type: 'json',
root: 'data',
record: 'entry'
}
}
});
Two differences with what you've done:
1/ The record option of the reader tells ExtJS to look for nested records in the json. It tells it to look for:
data: [
{
entry: {
id: 1,
title: "Title 1",
bought: true
}
}
]
instead of:
data: [
{
id: 1,
title: "Title 1",
bought: true
}
]
An alternative to setting the record property on the reader would be to disable nested json in Rails, by dumping this into your application config:
config.active_record.include_root_in_json = true
2/ When returning a single record, the json output should be the same as a collection, except you only have one record.
The Sencha Touch and ExtJS 4 docs can be a bit sparse sometimes, I found dissecting the examples is the best way to learn.
HTH
I ran into a similar problem when submitting a single record instead of a store. Setting a writer object with the name of your record as rootProperty solves the issue.
writer: {
type : 'json',
rootProperty : 'your_record'
}

Problem building JSON file

want to get all db entries by a specific hash and return it as json. I use the following code:
#tasks = Task.find_all_by_hash(params[:hash])
respond_to do |format|
format.json { render :json => #tasks }
end
now i have the problem that my json file isn't correct. it has the following output:
[
{
task: {
hash: "9dfca619f00f5488785f6b74ad1b590beefaee7a88c04884bf197e7679f3"
id: 4
created_at: "2010-12-16T09:09:51Z"
updated_at: "2010-12-16T09:14:10Z"
done: true
name: "Hallo"
}
},
{
task: {
hash: "9dfca619f00f5488785f6b74ad1b590beefaee7a88c04884bf197e7679f3"
id: 5
created_at: "2010-12-16T09:12:37Z"
updated_at: "2010-12-16T09:12:37Z"
done: true
name: "Test"
}
},
...
]
but actually i want it like this:
{ tasks: [
{"id":"1","date_added":"0001-02-22 00:00:00","post_content":"Check out my content, this is loaded via ajax and parsed with JSON","author":"Ryan Coughlin"},
{"id":"2","date_added":"0000-00-00 00:00:00","post_content":"More content, loaded. Lets try to add a form in and post via ajax and append the new data","author":"Billy Bob"}
]}
any advice? thank you!
Try collecting the task alone to an array and create a json object using that. Some thing like
#tasks = Task.find_all_by_hash(params[:hash])
a = []
#tasks.each do |t|
a << t[:task]
end
b = {:tasks => a}
respond_to do |format|
format.json { render :json => b }
end

Resources