Issue when rendering a Jbuilder json object as json - ruby-on-rails

I am developing in student tracking website in RoR. In model I have following code to build json
self.as_json
json = Jbuilder.new do |j|
j.courses student_courses do |course|
j.(course, :id, :name)
j.students students, :name
end
end.target!
puts json
return json
end
My controller code is
render json: {
courses: course.as_json,
}
and produces
{"courses":[
"{\"id\": 1,\"name\": \"english\",\"students\": [{\"name\": \"ALison\"},{\"name\": \"Robert\"}]
},{...}... ]"
instead of
"courses" : [
{
"id": 1,
"name": "english",
"students": [
{"name": "ALison"},
{"name": "Robert"}]
}, {..},...
]
It is adding escape character(/) before every double quotes. How can I solve this issue

Hey you can use this to generate as alternative
course.to_json(:include => { :students => { :only => :name } })

Related

Get all posts with all comments in json format

I have two models Post and Comment
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
How can I get all the posts with comments as below json response:
{
"posts": [
{
"name": "Ruby on Rails",
"comments": [
{
"desc": "awesome"
}
]
},
{
"name": "Java",
"comments": [
{
"desc": "Thanks"
},
{
"desc": "very useful"
}
]
}
]
}
try this, create a index.json.jbuilder in app/views/posts/ ,
and add the following code to it
json.posts #posts do |post|
json.name post.name
json.comments post.comments do |comment|
json.desc comment.desc
end
end
Use activemodel Activemodel Serializers.Also check out this railscasts video which gives you a pretty good idea about activemodel serializers and how to use it Railscasts-activemodel serializers.
Hope i've helped.

Active Model Serializer not working with json_api adapter

I am trying to use custom serializers for the relationships in a serializer and the json_api adapter enabled. However the relationships are not serialized correctly (or, better, not at all displayed/serialized).
PostController.rb
def index
render json: Post.all, each_serializer: Serializers::PostSerializer
end
Serializer
module Api
module V1
module Serializers
class PostSerializer < ActiveModel::Serializer
attributes :title, :id
belongs_to :author, serializer: UserSerializer
has_many :post_sections, serializer: PostSectionSerializer
end
end
end
end
JSON output:
{
"data": [
{
"attributes": {
"title": "Test Title"
},
"id": "1",
"relationships": {
"author": {
"data": {
"id": "1",
"type": "users"
}
},
"post_sections": {
"data": [
{
"id": "1",
"type": "post_sections"
}
]
}
},
"type": "posts"
}
]
}
As you can see, the relationships are not fulfilled, which happens only if I specify a custom serializer for the relationships!!
If I do something like this:
module Api
module V1
module Serializers
class PostSerializer < ActiveModel::Serializer
attributes :title, :id
belongs_to :author # no custom serializer!
has_many :post_sections # no custom serializer!
end
end
end
end
The relationships are shown correctly, but not using a custom serializer...
What's the issue here ?
EDIT
According to the json API 1.0 Format, what I am getting back is the so-called resource identifier object.
The following primary data is a single resource identifier object that
references the same resource:
{ "data": {
"type": "articles",
"id": "1" } }
Is there a way to prevent getting resource identifier objects, and get the actual data instead ?
Relationships only returns id and type according to json-api exmaples. If you need to return more information about this relation you should add include option on your render action.
Ex.
PostController.rb
class PostsController < ApplicationController
def show
render json: #post, include: 'comments'
end
end
Serializers
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :content
has_many :comment, serializer: CommentSerializer
end
class CommentSerializer < ActiveModel::Serializer
attributes :id, :title, :content
end
JSON output:
{
"data": {
"id": "1",
"type": "post",
"attributes": {
"title": "bla",
"content": "bla"
},
"relationships": {
"comment": {
"data": [
{
"type": "comments",
"id": "1"
}
]
}
}
},
"included": {
{
"id": "1",
"type": "comments",
"attributes": {
"title": "test",
"content": "test"
}
}
]
}
Just to add to #Bruno Bacarini's answer, you may also include chained associations by using:
render #posts, include: ['authors.profile', 'comments']
source: joaomdmoura's comment on github

Ruby map JSON multiple but not all attributes with RABL

I have the following Rabl in my views:
node(:relations) do |p|
related = p.relations.pluck(:related_to_id)
Spree::Product.find(related)
end
This renders the following:
"relations": [
{
"product": {
"id": 2,
"name": "T-SHIRT",
"description": "Awesome T shirts"
"created_at": "..."
"updated_at: "..."
.
.
.
bunch of other columns that I don't need.
My question is how do I only grab :name and :description, so that the JSON output looks like:
"relations": [
{
"product": {
"name": "T-SHIRT",
"description": "Awesome T shirts"
}
]
I've tried mapping it, Spree::Product.find(related).map { |r| [r.name, r.description] }
But that returns only the values, like so:
"relations": [
"T-SHIRT",
"Awesome T shirts"
]
Thank you for your help!
UPDATE:
When I write:
child :related_products do
attributes :name, :description
end
I get:
"spree_relations": [
{}
]
Link to model:
https://github.com/spree-contrib/spree_related_products/blob/master/app/models/spree/relation.rb
Well, there probably multiple ways to do it.
You can use rails #as_json method.
node(:relations) do |p|
related = p.relations.pluck(:related_to_id)
Spree::Product.find(related).as_json(only: [:name, :description])
end
Or you can try to do it the rabl way.
child :related_products do
attributes :name, :description
end
But for this you might need to change your model a bit.

Change variable name in jbuilder

I'm trying to get a JSON output using jbuilder that looks like this:
[{"correct_response": 0,
"section_id": 1,
"image_url": "https://850.s3.amazonaws.com/uploads/question/3/PreguntaWeb.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=r8COJLWNWABfwlm6BQ4ZpPlvFGw%3D&Expires=1401509509",
"responses": [{
"responseA": "https://850.s3.amazonaws.com/uploads/response/1/alternativaA.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=MkUUT7NuoHDH/BjiJdMiHV5f%2BB4%3D&Expires=1401509509"},
{"responseB": "https://850.s3.amazonaws.com/uploads/response/2/alternativaB.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=EZ6KeqhzlwPGX1PAetvqR/GPH2M%3D&Expires=1401509509"},
{"responseC": "https://850.s3.amazonaws.com/uploads/response/3/alternativaC.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=/Ntt6y4JrfVjjw0zpOKgIXtihvI%3D&Expires=1401509509"},
{"responseD": "https://850.s3.amazonaws.com/uploads/response/4/alternativaD.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=Exrr0WTsSx2n3FixjwADiiwwPjM%3D&Expires=1401509509"},
{"responseAE": "https://850.s3.amazonaws.com/uploads/response/5/alternativaE.png?AWSAccessKeyId=AKIAJSKXBBJ4URBWSPUQ&Signature=udJ5jK/zG9ug8A6WwsnhYZRcsPk%3D&Expires=1401509509"}
]
}]
I think I'm close, but I don't know what is wrong with my code:
json.array!(#questions) do |json, question|
json.extract! question, :correct_response, :section_id, :image_url
json.responses question.responses do |response|
[ 'responseA', 'responseB', 'responseC', 'responseD', 'responseE' ].each { |letter|
response.set!(letter, response.image_url )
}
end
end
end
Someone has any suggestions?

How to paginate Rabl's collections

I have this template:
# app/views/posts/index.rabl
collection #posts => :posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(#user) }
Witch returns:
{
"posts": [
{
"post": {
"id": 5,
"title": "...",
"subject": "...",
"user": {
"full_name": "..."
},
"read": true
}
}
]
}
And I would like to add to add some pagination params in order to rendering this:
{
"posts": [
{
"post": {
"id": 5,
"title": "...",
"subject": "...",
"user": {
"full_name": "..."
},
"read": true
}
}
],
"total": 42,
"total_pages": 12
}
Any ideas? Many thanks!
Sorry for my noob question, whitch was answered by the README. Here's an example of pagination:
object false
node(:total) {|m| #posts.total_count }
node(:total_pages) {|m| #posts.num_pages }
child(#posts) do
extends "api/v1/posts/show"
end
Note: I'm using Kaminari for pagination.
When searching for kaminari and rabl this is the first and pretty much only relevant result. As such, I would like to leave here a solution according to the HAL Specification that generates links like this.
So first, start with the view:
# api/v1/posts/index.rabl
object false
child(#posts) do
extends 'api/v1/posts/show'
end
node(:_links) do
paginate #posts
end
Then proceed to define the paginate method:
# app/helpers/api_helper
module ApiHelper
def paginate(collection)
current_page_num = collection.current_page
last_page_num = collection.total_pages
{
:first => first_page,
:previous => previous_page(current_page_num),
:self => current_page(current_page_num),
:next => next_page(current_page_num, last_page_num),
:last => last_page(last_page_num)
}
end
def first_page
{ :href => url_for(:page => 1) }
end
def previous_page(current_page_num)
return nil if current_page_num <= 1
{ :href => url_for(:page => current_page_num-1) }
end
def current_page(current_page_num)
{ :href => url_for(:page => current_page_num) }
end
def next_page(current_page_num, last_page_num)
return nil if current_page_num >= last_page_num
{ :href => url_for(:page => current_page_num+1) }
end
def last_page(last_page_num)
{ :href => url_for(:page => last_page_num) }
end
end
And finally, include the helper in the necessary controllers. The helper could be included in a Api::BaseController, from which all API controllers inherit:
helper :api
I could not have done this without Zag zag..'s solution, so.. Thank you so much!
note, for will_paginate 3.0.0 the following works:
node(:total) {|m| #posts.total_entries }
node(:total_pages) {|m| (#posts.total_entries.to_f / #posts.per_page).ceil }
node(:page_num){|m| #posts.current_page}
This might be what you are looking for ;)
object false
node :comments do
partial('posts/index', object: #posts)
end
node(:pagination) do
{
total:#posts.count,
total_pages: 20
}
end

Resources